@vizzly-testing/cli 0.19.2 → 0.20.1-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/api/client.js +134 -0
- package/dist/api/core.js +341 -0
- package/dist/api/endpoints.js +314 -0
- package/dist/api/index.js +19 -0
- package/dist/auth/client.js +91 -0
- package/dist/auth/core.js +176 -0
- package/dist/auth/index.js +30 -0
- package/dist/auth/operations.js +148 -0
- package/dist/cli.js +1 -1
- package/dist/client/index.js +0 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/finalize.js +41 -15
- package/dist/commands/login.js +7 -6
- package/dist/commands/logout.js +4 -4
- package/dist/commands/project.js +5 -4
- package/dist/commands/run.js +158 -90
- package/dist/commands/status.js +22 -18
- package/dist/commands/tdd.js +105 -78
- package/dist/commands/upload.js +61 -26
- package/dist/commands/whoami.js +4 -4
- package/dist/config/core.js +438 -0
- package/dist/config/index.js +13 -0
- package/dist/config/operations.js +327 -0
- package/dist/index.js +1 -1
- package/dist/project/core.js +295 -0
- package/dist/project/index.js +13 -0
- package/dist/project/operations.js +393 -0
- package/dist/report-generator/core.js +315 -0
- package/dist/report-generator/index.js +8 -0
- package/dist/report-generator/operations.js +196 -0
- package/dist/reporter/reporter-bundle.iife.js +16 -16
- package/dist/screenshot-server/core.js +157 -0
- package/dist/screenshot-server/index.js +11 -0
- package/dist/screenshot-server/operations.js +183 -0
- package/dist/sdk/index.js +3 -2
- package/dist/server/handlers/api-handler.js +14 -5
- package/dist/server/handlers/tdd-handler.js +80 -48
- package/dist/server-manager/core.js +183 -0
- package/dist/server-manager/index.js +81 -0
- package/dist/server-manager/operations.js +208 -0
- package/dist/services/build-manager.js +2 -69
- package/dist/services/index.js +21 -48
- package/dist/services/screenshot-server.js +40 -74
- package/dist/services/server-manager.js +45 -80
- package/dist/services/static-report-generator.js +21 -163
- package/dist/services/test-runner.js +90 -249
- package/dist/services/uploader.js +56 -358
- package/dist/tdd/core/hotspot-coverage.js +112 -0
- package/dist/tdd/core/signature.js +101 -0
- package/dist/tdd/index.js +19 -0
- package/dist/tdd/metadata/baseline-metadata.js +103 -0
- package/dist/tdd/metadata/hotspot-metadata.js +93 -0
- package/dist/tdd/services/baseline-downloader.js +151 -0
- package/dist/tdd/services/baseline-manager.js +166 -0
- package/dist/tdd/services/comparison-service.js +230 -0
- package/dist/tdd/services/hotspot-service.js +71 -0
- package/dist/tdd/services/result-service.js +123 -0
- package/dist/tdd/tdd-service.js +1081 -0
- package/dist/test-runner/core.js +255 -0
- package/dist/test-runner/index.js +13 -0
- package/dist/test-runner/operations.js +483 -0
- package/dist/types/client.d.ts +4 -2
- package/dist/types/index.d.ts +5 -0
- package/dist/uploader/core.js +396 -0
- package/dist/uploader/index.js +11 -0
- package/dist/uploader/operations.js +412 -0
- package/dist/utils/config-schema.js +8 -3
- package/package.json +7 -12
- package/dist/services/api-service.js +0 -412
- package/dist/services/auth-service.js +0 -226
- package/dist/services/config-service.js +0 -369
- package/dist/services/html-report-generator.js +0 -455
- package/dist/services/project-service.js +0 -326
- package/dist/services/report-generator/report.css +0 -411
- package/dist/services/report-generator/viewer.js +0 -102
- package/dist/services/tdd-service.js +0 -1429
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uploader Operations - I/O operations with dependency injection
|
|
3
|
+
*
|
|
4
|
+
* Each operation takes its dependencies as parameters for testability.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { buildBuildInfo, buildCompletedProgress, buildDeduplicationProgress, buildFileMetadata, buildProcessingProgress, buildScanningProgress, buildScreenshotPattern, buildUploadingProgress, buildUploadResult, buildWaitResult, DEFAULT_SHA_CHECK_BATCH_SIZE, extractStatusCodeFromError, fileToScreenshotFormat, getElapsedTime, isTimedOut, partitionFilesByExistence, validateApiKey, validateDirectoryStats, validateFilesFound, validateScreenshotsDir } from './core.js';
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// File Discovery
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Find all PNG screenshots in a directory
|
|
15
|
+
* @param {Object} options - Options
|
|
16
|
+
* @param {string} options.directory - Directory to search
|
|
17
|
+
* @param {Object} options.deps - Dependencies
|
|
18
|
+
* @param {Function} options.deps.glob - Glob function
|
|
19
|
+
* @returns {Promise<Array<string>>} Array of file paths
|
|
20
|
+
*/
|
|
21
|
+
export async function findScreenshots({
|
|
22
|
+
directory,
|
|
23
|
+
deps
|
|
24
|
+
}) {
|
|
25
|
+
let {
|
|
26
|
+
glob
|
|
27
|
+
} = deps;
|
|
28
|
+
let pattern = buildScreenshotPattern(directory);
|
|
29
|
+
return glob(pattern, {
|
|
30
|
+
absolute: true
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// File Processing
|
|
36
|
+
// ============================================================================
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Process files to extract metadata and compute hashes
|
|
40
|
+
* @param {Object} options - Options
|
|
41
|
+
* @param {Array<string>} options.files - File paths
|
|
42
|
+
* @param {AbortSignal} options.signal - Abort signal
|
|
43
|
+
* @param {Function} options.onProgress - Progress callback
|
|
44
|
+
* @param {Object} options.deps - Dependencies
|
|
45
|
+
* @param {Function} options.deps.readFile - File read function
|
|
46
|
+
* @param {Function} options.deps.createError - Error factory
|
|
47
|
+
* @returns {Promise<Array>} File metadata array
|
|
48
|
+
*/
|
|
49
|
+
export async function processFiles({
|
|
50
|
+
files,
|
|
51
|
+
signal,
|
|
52
|
+
onProgress,
|
|
53
|
+
deps
|
|
54
|
+
}) {
|
|
55
|
+
let {
|
|
56
|
+
readFile,
|
|
57
|
+
createError
|
|
58
|
+
} = deps;
|
|
59
|
+
let results = [];
|
|
60
|
+
let count = 0;
|
|
61
|
+
for (let filePath of files) {
|
|
62
|
+
if (signal.aborted) {
|
|
63
|
+
throw createError('Operation cancelled', 'UPLOAD_CANCELLED');
|
|
64
|
+
}
|
|
65
|
+
let buffer = await readFile(filePath);
|
|
66
|
+
let metadata = buildFileMetadata(filePath, buffer);
|
|
67
|
+
results.push(metadata);
|
|
68
|
+
count++;
|
|
69
|
+
if (count % 10 === 0 || count === files.length) {
|
|
70
|
+
onProgress(count);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return results;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// SHA Checking / Deduplication
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check which files already exist on the server
|
|
82
|
+
* @param {Object} options - Options
|
|
83
|
+
* @param {Array} options.fileMetadata - File metadata array
|
|
84
|
+
* @param {Object} options.client - API client
|
|
85
|
+
* @param {AbortSignal} options.signal - Abort signal
|
|
86
|
+
* @param {string} options.buildId - Build ID
|
|
87
|
+
* @param {Object} options.deps - Dependencies
|
|
88
|
+
* @param {Function} options.deps.checkShas - SHA check API function
|
|
89
|
+
* @param {Function} options.deps.createError - Error factory
|
|
90
|
+
* @param {Object} options.deps.output - Output utilities
|
|
91
|
+
* @returns {Promise<{ toUpload: Array, existing: Array, screenshots: Array }>}
|
|
92
|
+
*/
|
|
93
|
+
export async function checkExistingFiles({
|
|
94
|
+
fileMetadata,
|
|
95
|
+
client,
|
|
96
|
+
signal,
|
|
97
|
+
buildId,
|
|
98
|
+
deps
|
|
99
|
+
}) {
|
|
100
|
+
let {
|
|
101
|
+
checkShas,
|
|
102
|
+
createError,
|
|
103
|
+
output
|
|
104
|
+
} = deps;
|
|
105
|
+
let existingShas = new Set();
|
|
106
|
+
let allScreenshots = [];
|
|
107
|
+
for (let i = 0; i < fileMetadata.length; i += DEFAULT_SHA_CHECK_BATCH_SIZE) {
|
|
108
|
+
if (signal.aborted) {
|
|
109
|
+
throw createError('Operation cancelled', 'UPLOAD_CANCELLED');
|
|
110
|
+
}
|
|
111
|
+
let batch = fileMetadata.slice(i, i + DEFAULT_SHA_CHECK_BATCH_SIZE);
|
|
112
|
+
let screenshotBatch = batch.map(fileToScreenshotFormat);
|
|
113
|
+
try {
|
|
114
|
+
let res = await checkShas(client, screenshotBatch, buildId);
|
|
115
|
+
let {
|
|
116
|
+
existing = [],
|
|
117
|
+
screenshots = []
|
|
118
|
+
} = res || {};
|
|
119
|
+
for (let sha of existing) {
|
|
120
|
+
existingShas.add(sha);
|
|
121
|
+
}
|
|
122
|
+
allScreenshots.push(...screenshots);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
output.debug('upload', 'SHA check failed, continuing without deduplication', {
|
|
125
|
+
error: error.message
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
let partitioned = partitionFilesByExistence(fileMetadata, existingShas);
|
|
130
|
+
return {
|
|
131
|
+
toUpload: partitioned.toUpload,
|
|
132
|
+
existing: partitioned.existing,
|
|
133
|
+
screenshots: allScreenshots
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// File Upload
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Upload files to Vizzly
|
|
143
|
+
* @param {Object} options - Options
|
|
144
|
+
* @param {Array} options.toUpload - Files to upload
|
|
145
|
+
* @param {string} options.buildId - Build ID
|
|
146
|
+
* @param {Object} options.client - API client
|
|
147
|
+
* @param {AbortSignal} options.signal - Abort signal
|
|
148
|
+
* @param {number} options.batchSize - Batch size
|
|
149
|
+
* @param {Function} options.onProgress - Progress callback
|
|
150
|
+
* @param {Object} options.deps - Dependencies
|
|
151
|
+
* @param {Function} options.deps.createError - Error factory
|
|
152
|
+
* @returns {Promise<{ buildId: string, url: string|null }>}
|
|
153
|
+
*/
|
|
154
|
+
export async function uploadFiles({
|
|
155
|
+
toUpload,
|
|
156
|
+
buildId,
|
|
157
|
+
client,
|
|
158
|
+
signal,
|
|
159
|
+
batchSize,
|
|
160
|
+
onProgress,
|
|
161
|
+
deps
|
|
162
|
+
}) {
|
|
163
|
+
let {
|
|
164
|
+
createError
|
|
165
|
+
} = deps;
|
|
166
|
+
let result = null;
|
|
167
|
+
if (toUpload.length === 0) {
|
|
168
|
+
return {
|
|
169
|
+
buildId,
|
|
170
|
+
url: null
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
for (let i = 0; i < toUpload.length; i += batchSize) {
|
|
174
|
+
if (signal.aborted) {
|
|
175
|
+
throw createError('Operation cancelled', 'UPLOAD_CANCELLED');
|
|
176
|
+
}
|
|
177
|
+
let batch = toUpload.slice(i, i + batchSize);
|
|
178
|
+
let form = new FormData();
|
|
179
|
+
form.append('build_id', buildId);
|
|
180
|
+
for (let file of batch) {
|
|
181
|
+
let blob = new Blob([file.buffer], {
|
|
182
|
+
type: 'image/png'
|
|
183
|
+
});
|
|
184
|
+
form.append('screenshots', blob, file.filename);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
result = await client.request('/api/sdk/upload', {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
body: form,
|
|
190
|
+
signal,
|
|
191
|
+
headers: {}
|
|
192
|
+
});
|
|
193
|
+
} catch (err) {
|
|
194
|
+
throw createError(`Upload failed: ${err.message}`, 'UPLOAD_FAILED', {
|
|
195
|
+
batch: Math.floor(i / batchSize) + 1
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
onProgress(i + batch.length);
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
buildId,
|
|
202
|
+
url: result?.build?.url || result?.url
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ============================================================================
|
|
207
|
+
// Build Waiting
|
|
208
|
+
// ============================================================================
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Wait for a build to complete
|
|
212
|
+
* @param {Object} options - Options
|
|
213
|
+
* @param {string} options.buildId - Build ID
|
|
214
|
+
* @param {number} options.timeout - Timeout in ms
|
|
215
|
+
* @param {AbortSignal} options.signal - Abort signal
|
|
216
|
+
* @param {Object} options.client - API client
|
|
217
|
+
* @param {Object} options.deps - Dependencies
|
|
218
|
+
* @param {Function} options.deps.createError - Error factory
|
|
219
|
+
* @param {Function} options.deps.createTimeoutError - Timeout error factory
|
|
220
|
+
* @returns {Promise<Object>} Build result
|
|
221
|
+
*/
|
|
222
|
+
export async function waitForBuild({
|
|
223
|
+
buildId,
|
|
224
|
+
timeout,
|
|
225
|
+
signal,
|
|
226
|
+
client,
|
|
227
|
+
deps
|
|
228
|
+
}) {
|
|
229
|
+
let {
|
|
230
|
+
createError,
|
|
231
|
+
createTimeoutError
|
|
232
|
+
} = deps;
|
|
233
|
+
let startTime = Date.now();
|
|
234
|
+
while (!isTimedOut(startTime, timeout)) {
|
|
235
|
+
if (signal.aborted) {
|
|
236
|
+
throw createError('Operation cancelled', 'UPLOAD_CANCELLED', {
|
|
237
|
+
buildId
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
let resp;
|
|
241
|
+
try {
|
|
242
|
+
resp = await client.request(`/api/sdk/builds/${buildId}`, {
|
|
243
|
+
signal
|
|
244
|
+
});
|
|
245
|
+
} catch (err) {
|
|
246
|
+
let code = extractStatusCodeFromError(err?.message);
|
|
247
|
+
throw createError(`Failed to check build status: ${code}`, 'BUILD_STATUS_FAILED');
|
|
248
|
+
}
|
|
249
|
+
let build = resp?.build ?? resp;
|
|
250
|
+
if (build.status === 'completed') {
|
|
251
|
+
return buildWaitResult(build);
|
|
252
|
+
}
|
|
253
|
+
if (build.status === 'failed') {
|
|
254
|
+
throw createError(`Build failed: ${build.error || 'Unknown error'}`, 'BUILD_FAILED');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
throw createTimeoutError(`Build timed out after ${timeout}ms`, {
|
|
258
|
+
buildId,
|
|
259
|
+
timeout,
|
|
260
|
+
elapsed: getElapsedTime(startTime)
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ============================================================================
|
|
265
|
+
// Main Upload Operation
|
|
266
|
+
// ============================================================================
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Upload screenshots to Vizzly
|
|
270
|
+
* @param {Object} options - Options
|
|
271
|
+
* @param {Object} options.uploadOptions - Upload options (screenshotsDir, buildName, etc.)
|
|
272
|
+
* @param {Object} options.config - Configuration
|
|
273
|
+
* @param {AbortSignal} options.signal - Abort signal
|
|
274
|
+
* @param {number} options.batchSize - Batch size
|
|
275
|
+
* @param {Object} options.deps - Dependencies
|
|
276
|
+
* @returns {Promise<Object>} Upload result
|
|
277
|
+
*/
|
|
278
|
+
export async function upload({
|
|
279
|
+
uploadOptions,
|
|
280
|
+
config,
|
|
281
|
+
signal,
|
|
282
|
+
batchSize,
|
|
283
|
+
deps
|
|
284
|
+
}) {
|
|
285
|
+
let {
|
|
286
|
+
client,
|
|
287
|
+
createBuild,
|
|
288
|
+
getDefaultBranch,
|
|
289
|
+
glob,
|
|
290
|
+
readFile,
|
|
291
|
+
stat,
|
|
292
|
+
checkShas,
|
|
293
|
+
createError,
|
|
294
|
+
createValidationError,
|
|
295
|
+
createUploadError,
|
|
296
|
+
output
|
|
297
|
+
} = deps;
|
|
298
|
+
let {
|
|
299
|
+
screenshotsDir,
|
|
300
|
+
onProgress = () => {}
|
|
301
|
+
} = uploadOptions;
|
|
302
|
+
try {
|
|
303
|
+
// Validate API key
|
|
304
|
+
let apiKeyValidation = validateApiKey(config.apiKey);
|
|
305
|
+
if (!apiKeyValidation.valid) {
|
|
306
|
+
throw createValidationError(apiKeyValidation.error, {
|
|
307
|
+
config: {
|
|
308
|
+
apiKey: config.apiKey,
|
|
309
|
+
apiUrl: config.apiUrl
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Validate screenshots directory
|
|
315
|
+
let dirValidation = validateScreenshotsDir(screenshotsDir);
|
|
316
|
+
if (!dirValidation.valid) {
|
|
317
|
+
throw createValidationError(dirValidation.error);
|
|
318
|
+
}
|
|
319
|
+
let stats = await stat(screenshotsDir);
|
|
320
|
+
let statsValidation = validateDirectoryStats(stats, screenshotsDir);
|
|
321
|
+
if (!statsValidation.valid) {
|
|
322
|
+
throw createValidationError(statsValidation.error);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Find screenshots
|
|
326
|
+
let files = await findScreenshots({
|
|
327
|
+
directory: screenshotsDir,
|
|
328
|
+
deps: {
|
|
329
|
+
glob
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
let filesValidation = validateFilesFound(files, screenshotsDir);
|
|
333
|
+
if (!filesValidation.valid) {
|
|
334
|
+
throw createUploadError(filesValidation.error, filesValidation.context);
|
|
335
|
+
}
|
|
336
|
+
onProgress(buildScanningProgress(files.length));
|
|
337
|
+
|
|
338
|
+
// Process files
|
|
339
|
+
let fileMetadata = await processFiles({
|
|
340
|
+
files,
|
|
341
|
+
signal,
|
|
342
|
+
onProgress: current => onProgress(buildProcessingProgress(current, files.length)),
|
|
343
|
+
deps: {
|
|
344
|
+
readFile,
|
|
345
|
+
createError
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Create build
|
|
350
|
+
let defaultBranch = await getDefaultBranch();
|
|
351
|
+
let buildInfo = buildBuildInfo(uploadOptions, defaultBranch);
|
|
352
|
+
let build = await createBuild(client, buildInfo);
|
|
353
|
+
let buildId = build.id;
|
|
354
|
+
|
|
355
|
+
// Check existing files
|
|
356
|
+
let {
|
|
357
|
+
toUpload,
|
|
358
|
+
existing,
|
|
359
|
+
screenshots
|
|
360
|
+
} = await checkExistingFiles({
|
|
361
|
+
fileMetadata,
|
|
362
|
+
client,
|
|
363
|
+
signal,
|
|
364
|
+
buildId,
|
|
365
|
+
deps: {
|
|
366
|
+
checkShas,
|
|
367
|
+
createError,
|
|
368
|
+
output
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
onProgress(buildDeduplicationProgress(toUpload.length, existing.length, files.length));
|
|
372
|
+
|
|
373
|
+
// Upload files
|
|
374
|
+
let result = await uploadFiles({
|
|
375
|
+
toUpload,
|
|
376
|
+
existing,
|
|
377
|
+
screenshots,
|
|
378
|
+
buildId,
|
|
379
|
+
buildInfo,
|
|
380
|
+
client,
|
|
381
|
+
signal,
|
|
382
|
+
batchSize,
|
|
383
|
+
onProgress: current => onProgress(buildUploadingProgress(current, toUpload.length)),
|
|
384
|
+
deps: {
|
|
385
|
+
createError
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
onProgress(buildCompletedProgress(result.buildId, result.url));
|
|
389
|
+
return buildUploadResult({
|
|
390
|
+
buildId: result.buildId,
|
|
391
|
+
url: result.url,
|
|
392
|
+
total: files.length,
|
|
393
|
+
uploaded: toUpload.length,
|
|
394
|
+
skipped: existing.length
|
|
395
|
+
});
|
|
396
|
+
} catch (error) {
|
|
397
|
+
output.debug('upload', 'failed', {
|
|
398
|
+
error: error.message
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Re-throw if already a VizzlyError
|
|
402
|
+
if (error.name?.includes('Error') && error.code) {
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Wrap unknown errors
|
|
407
|
+
throw createUploadError(`Upload failed: ${error.message}`, {
|
|
408
|
+
originalError: error.message,
|
|
409
|
+
stack: error.stack
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
@@ -36,9 +36,11 @@ const uploadSchema = z.object({
|
|
|
36
36
|
/**
|
|
37
37
|
* Comparison configuration schema
|
|
38
38
|
* threshold: CIEDE2000 Delta E units (0.0 = exact, 1.0 = JND, 2.0 = recommended, 3.0+ = permissive)
|
|
39
|
+
* minClusterSize: pixels (1 = exact)
|
|
39
40
|
*/
|
|
40
41
|
const comparisonSchema = z.object({
|
|
41
|
-
threshold: z.number().min(0).default(2.0)
|
|
42
|
+
threshold: z.number().min(0).default(2.0),
|
|
43
|
+
minClusterSize: z.int().min(1).default(2)
|
|
42
44
|
});
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -70,11 +72,13 @@ export const vizzlyConfigSchema = z.object({
|
|
|
70
72
|
timeout: 30000
|
|
71
73
|
}),
|
|
72
74
|
comparison: comparisonSchema.default({
|
|
73
|
-
threshold: 2.0
|
|
75
|
+
threshold: 2.0,
|
|
76
|
+
minClusterSize: 2
|
|
74
77
|
}),
|
|
75
78
|
tdd: tddSchema.default({
|
|
76
79
|
openReport: false
|
|
77
80
|
}),
|
|
81
|
+
signatureProperties: z.array(z.string()).default([]),
|
|
78
82
|
plugins: z.array(z.string()).default([]),
|
|
79
83
|
// Additional optional fields
|
|
80
84
|
parallelId: z.string().optional(),
|
|
@@ -99,7 +103,8 @@ export const vizzlyConfigSchema = z.object({
|
|
|
99
103
|
timeout: 30000
|
|
100
104
|
},
|
|
101
105
|
comparison: {
|
|
102
|
-
threshold: 2.0
|
|
106
|
+
threshold: 2.0,
|
|
107
|
+
minClusterSize: 2
|
|
103
108
|
},
|
|
104
109
|
tdd: {
|
|
105
110
|
openReport: false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizzly-testing/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.1-beta.0",
|
|
4
4
|
"description": "Visual review platform for UI developers and designers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"visual-testing",
|
|
@@ -58,25 +58,22 @@
|
|
|
58
58
|
],
|
|
59
59
|
"scripts": {
|
|
60
60
|
"start": "node src/index.js",
|
|
61
|
-
"build": "npm run clean && npm run compile && npm run
|
|
61
|
+
"build": "npm run clean && npm run compile && npm run build:reporter && npm run copy-types",
|
|
62
62
|
"clean": "rimraf dist",
|
|
63
63
|
"compile": "babel src --out-dir dist --ignore '**/*.test.js'",
|
|
64
|
-
"copy-assets": "cp src/services/report-generator/report.css dist/services/report-generator/",
|
|
65
64
|
"copy-types": "mkdir -p dist/types && cp src/types/*.d.ts dist/types/",
|
|
66
65
|
"build:reporter": "cd src/reporter && vite build",
|
|
67
66
|
"dev:reporter": "cd src/reporter && vite --config vite.dev.config.js",
|
|
68
67
|
"test:types": "tsd",
|
|
69
68
|
"prepublishOnly": "npm run build",
|
|
70
|
-
"test": "
|
|
71
|
-
"test:watch": "
|
|
69
|
+
"test": "node --experimental-test-coverage --test --test-name-pattern='.*' $(find tests -name '*.test.js')",
|
|
70
|
+
"test:watch": "node --test --watch $(find tests -name '*.test.js')",
|
|
72
71
|
"test:reporter": "playwright test --config=tests/reporter/playwright.config.js",
|
|
73
72
|
"test:reporter:visual": "node bin/vizzly.js run \"npm run test:reporter\"",
|
|
74
|
-
"lint": "biome
|
|
75
|
-
"lint:fix": "biome
|
|
73
|
+
"lint": "biome check src tests",
|
|
74
|
+
"lint:fix": "biome check --write src tests",
|
|
76
75
|
"format": "biome format --write src tests",
|
|
77
|
-
"format:check": "biome format src tests"
|
|
78
|
-
"check": "biome check src tests",
|
|
79
|
-
"check:fix": "biome check --write src tests"
|
|
76
|
+
"format:check": "biome format src tests"
|
|
80
77
|
},
|
|
81
78
|
"engines": {
|
|
82
79
|
"node": ">=22.0.0"
|
|
@@ -118,7 +115,6 @@
|
|
|
118
115
|
"@tanstack/react-query": "^5.90.11",
|
|
119
116
|
"@types/node": "^24.10.1",
|
|
120
117
|
"@vitejs/plugin-react": "^5.0.3",
|
|
121
|
-
"@vitest/coverage-v8": "^4.0.3",
|
|
122
118
|
"autoprefixer": "^10.4.21",
|
|
123
119
|
"babel-plugin-transform-remove-console": "^6.9.4",
|
|
124
120
|
"postcss": "^8.5.6",
|
|
@@ -129,7 +125,6 @@
|
|
|
129
125
|
"tsd": "^0.33.0",
|
|
130
126
|
"typescript": "^5.0.4",
|
|
131
127
|
"vite": "^7.1.7",
|
|
132
|
-
"vitest": "^4.0.3",
|
|
133
128
|
"wouter": "^3.7.1"
|
|
134
129
|
}
|
|
135
130
|
}
|