@vizzly-testing/cli 0.14.0 → 0.15.1
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/cli.js +70 -68
- package/dist/commands/doctor.js +30 -34
- package/dist/commands/finalize.js +24 -23
- package/dist/commands/init.js +30 -28
- package/dist/commands/login.js +49 -55
- package/dist/commands/logout.js +14 -19
- package/dist/commands/project.js +83 -103
- package/dist/commands/run.js +77 -89
- package/dist/commands/status.js +48 -49
- package/dist/commands/tdd-daemon.js +90 -86
- package/dist/commands/tdd.js +59 -88
- package/dist/commands/upload.js +57 -57
- package/dist/commands/whoami.js +40 -45
- package/dist/index.js +2 -5
- package/dist/plugin-loader.js +15 -17
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +74 -41
- package/dist/sdk/index.js +36 -45
- package/dist/server/handlers/api-handler.js +14 -15
- package/dist/server/handlers/tdd-handler.js +34 -37
- package/dist/server/http-server.js +75 -869
- package/dist/server/middleware/cors.js +22 -0
- package/dist/server/middleware/json-parser.js +35 -0
- package/dist/server/middleware/response.js +79 -0
- package/dist/server/routers/assets.js +91 -0
- package/dist/server/routers/auth.js +144 -0
- package/dist/server/routers/baseline.js +163 -0
- package/dist/server/routers/cloud-proxy.js +146 -0
- package/dist/server/routers/config.js +126 -0
- package/dist/server/routers/dashboard.js +130 -0
- package/dist/server/routers/health.js +61 -0
- package/dist/server/routers/projects.js +168 -0
- package/dist/server/routers/screenshot.js +86 -0
- package/dist/services/auth-service.js +1 -1
- package/dist/services/build-manager.js +13 -40
- package/dist/services/config-service.js +2 -4
- package/dist/services/html-report-generator.js +6 -5
- package/dist/services/index.js +64 -0
- package/dist/services/project-service.js +121 -40
- package/dist/services/screenshot-server.js +9 -9
- package/dist/services/server-manager.js +11 -18
- package/dist/services/static-report-generator.js +3 -4
- package/dist/services/tdd-service.js +246 -103
- package/dist/services/test-runner.js +24 -25
- package/dist/services/uploader.js +5 -4
- package/dist/types/commands/init.d.ts +1 -2
- package/dist/types/index.d.ts +2 -3
- package/dist/types/plugin-loader.d.ts +1 -2
- package/dist/types/reporter/src/api/client.d.ts +178 -0
- package/dist/types/reporter/src/components/app-router.d.ts +1 -3
- package/dist/types/reporter/src/components/code-block.d.ts +4 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/onion-skin-mode.d.ts +10 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/overlay-mode.d.ts +11 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/shared/base-comparison-mode.d.ts +14 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/shared/image-renderer.d.ts +30 -0
- package/dist/types/reporter/src/components/comparison/comparison-modes/toggle-view.d.ts +8 -0
- package/dist/types/reporter/src/components/comparison/comparison-viewer.d.ts +4 -0
- package/dist/types/reporter/src/components/comparison/screenshot-display.d.ts +16 -0
- package/dist/types/reporter/src/components/design-system/alert.d.ts +9 -0
- package/dist/types/reporter/src/components/design-system/badge.d.ts +17 -0
- package/dist/types/reporter/src/components/design-system/button.d.ts +19 -0
- package/dist/types/reporter/src/components/design-system/card.d.ts +31 -0
- package/dist/types/reporter/src/components/design-system/empty-state.d.ts +13 -0
- package/dist/types/reporter/src/components/design-system/form-controls.d.ts +44 -0
- package/dist/types/reporter/src/components/design-system/health-ring.d.ts +7 -0
- package/dist/types/reporter/src/components/design-system/index.d.ts +11 -0
- package/dist/types/reporter/src/components/design-system/modal.d.ts +10 -0
- package/dist/types/reporter/src/components/design-system/skeleton.d.ts +19 -0
- package/dist/types/reporter/src/components/design-system/spinner.d.ts +10 -0
- package/dist/types/reporter/src/components/design-system/tabs.d.ts +13 -0
- package/dist/types/reporter/src/components/layout/header.d.ts +5 -0
- package/dist/types/reporter/src/components/layout/index.d.ts +2 -0
- package/dist/types/reporter/src/components/layout/layout.d.ts +6 -0
- package/dist/types/reporter/src/components/views/builds-view.d.ts +1 -0
- package/dist/types/reporter/src/components/views/comparison-detail-view.d.ts +1 -4
- package/dist/types/reporter/src/components/views/comparisons-view.d.ts +1 -6
- package/dist/types/reporter/src/components/views/stats-view.d.ts +1 -6
- package/dist/types/reporter/src/components/waiting-for-screenshots.d.ts +1 -0
- package/dist/types/reporter/src/hooks/queries/use-auth-queries.d.ts +15 -0
- package/dist/types/reporter/src/hooks/queries/use-cloud-queries.d.ts +6 -0
- package/dist/types/reporter/src/hooks/queries/use-config-queries.d.ts +6 -0
- package/dist/types/reporter/src/hooks/queries/use-tdd-queries.d.ts +9 -0
- package/dist/types/reporter/src/lib/query-client.d.ts +2 -0
- package/dist/types/reporter/src/lib/query-keys.d.ts +13 -0
- package/dist/types/sdk/index.d.ts +2 -4
- package/dist/types/server/handlers/tdd-handler.d.ts +2 -0
- package/dist/types/server/http-server.d.ts +1 -1
- package/dist/types/server/middleware/cors.d.ts +11 -0
- package/dist/types/server/middleware/json-parser.d.ts +10 -0
- package/dist/types/server/middleware/response.d.ts +50 -0
- package/dist/types/server/routers/assets.d.ts +6 -0
- package/dist/types/server/routers/auth.d.ts +9 -0
- package/dist/types/server/routers/baseline.d.ts +13 -0
- package/dist/types/server/routers/cloud-proxy.d.ts +11 -0
- package/dist/types/server/routers/config.d.ts +9 -0
- package/dist/types/server/routers/dashboard.d.ts +6 -0
- package/dist/types/server/routers/health.d.ts +11 -0
- package/dist/types/server/routers/projects.d.ts +9 -0
- package/dist/types/server/routers/screenshot.d.ts +11 -0
- package/dist/types/services/build-manager.d.ts +4 -3
- package/dist/types/services/config-service.d.ts +2 -3
- package/dist/types/services/index.d.ts +7 -0
- package/dist/types/services/project-service.d.ts +6 -4
- package/dist/types/services/screenshot-server.d.ts +5 -5
- package/dist/types/services/server-manager.d.ts +5 -3
- package/dist/types/services/tdd-service.d.ts +12 -1
- package/dist/types/services/test-runner.d.ts +3 -3
- package/dist/types/utils/output.d.ts +84 -0
- package/dist/utils/config-loader.js +24 -48
- package/dist/utils/global-config.js +2 -17
- package/dist/utils/output.js +445 -0
- package/dist/utils/security.js +3 -4
- package/docs/api-reference.md +0 -1
- package/docs/plugins.md +33 -34
- package/package.json +3 -2
- package/dist/container/index.js +0 -215
- package/dist/services/base-service.js +0 -154
- package/dist/types/container/index.d.ts +0 -59
- package/dist/types/reporter/src/components/comparison/viewer-modes/onion-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/overlay-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/side-by-side-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/comparison/viewer-modes/toggle-viewer.d.ts +0 -3
- package/dist/types/reporter/src/components/dashboard/dashboard-header.d.ts +0 -5
- package/dist/types/reporter/src/components/dashboard/dashboard-stats.d.ts +0 -4
- package/dist/types/reporter/src/components/dashboard/empty-state.d.ts +0 -8
- package/dist/types/reporter/src/components/ui/form-field.d.ts +0 -16
- package/dist/types/reporter/src/components/ui/status-badge.d.ts +0 -5
- package/dist/types/reporter/src/hooks/use-auth.d.ts +0 -10
- package/dist/types/reporter/src/hooks/use-baseline-actions.d.ts +0 -5
- package/dist/types/reporter/src/hooks/use-config.d.ts +0 -9
- package/dist/types/reporter/src/hooks/use-projects.d.ts +0 -10
- package/dist/types/reporter/src/hooks/use-report-data.d.ts +0 -7
- package/dist/types/reporter/src/hooks/use-vizzly-api.d.ts +0 -9
- package/dist/types/services/base-service.d.ts +0 -71
- package/dist/types/utils/console-ui.d.ts +0 -61
- package/dist/types/utils/logger-factory.d.ts +0 -26
- package/dist/types/utils/logger.d.ts +0 -79
- package/dist/utils/console-ui.js +0 -241
- package/dist/utils/logger-factory.js +0 -76
- package/dist/utils/logger.js +0 -231
package/dist/sdk/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createUploader } from '../services/uploader.js';
|
|
|
16
16
|
import { createTDDService } from '../services/tdd-service.js';
|
|
17
17
|
import { ScreenshotServer } from '../services/screenshot-server.js';
|
|
18
18
|
import { loadConfig } from '../utils/config-loader.js';
|
|
19
|
-
import
|
|
19
|
+
import * as output from '../utils/output.js';
|
|
20
20
|
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -53,21 +53,21 @@ import { VizzlyError } from '../errors/vizzly-error.js';
|
|
|
53
53
|
* await vizzly.stop();
|
|
54
54
|
*/
|
|
55
55
|
export function createVizzly(config = {}, options = {}) {
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
// Configure output based on options
|
|
57
|
+
output.configure({
|
|
58
58
|
verbose: options.verbose || false
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
// Merge with loaded config
|
|
62
|
-
|
|
62
|
+
let resolvedConfig = {
|
|
63
63
|
...config
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* Initialize SDK with config loading
|
|
68
68
|
*/
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
let init = async () => {
|
|
70
|
+
let fileConfig = await loadConfig();
|
|
71
71
|
Object.assign(resolvedConfig, fileConfig, config); // CLI config takes precedence
|
|
72
72
|
return resolvedConfig;
|
|
73
73
|
};
|
|
@@ -75,41 +75,39 @@ export function createVizzly(config = {}, options = {}) {
|
|
|
75
75
|
/**
|
|
76
76
|
* Create uploader service
|
|
77
77
|
*/
|
|
78
|
-
|
|
78
|
+
let createUploaderService = (uploaderOptions = {}) => {
|
|
79
79
|
return createUploader({
|
|
80
80
|
apiKey: resolvedConfig.apiKey,
|
|
81
81
|
apiUrl: resolvedConfig.apiUrl
|
|
82
82
|
}, {
|
|
83
83
|
...options,
|
|
84
|
-
...uploaderOptions
|
|
85
|
-
logger
|
|
84
|
+
...uploaderOptions
|
|
86
85
|
});
|
|
87
86
|
};
|
|
88
87
|
|
|
89
88
|
/**
|
|
90
89
|
* Create TDD service
|
|
91
90
|
*/
|
|
92
|
-
|
|
91
|
+
let createTDDServiceInstance = (tddOptions = {}) => {
|
|
93
92
|
return createTDDService(resolvedConfig, {
|
|
94
93
|
...options,
|
|
95
|
-
...tddOptions
|
|
96
|
-
logger
|
|
94
|
+
...tddOptions
|
|
97
95
|
});
|
|
98
96
|
};
|
|
99
97
|
|
|
100
98
|
/**
|
|
101
99
|
* Upload screenshots (convenience method)
|
|
102
100
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
let upload = async uploadOptions => {
|
|
102
|
+
let uploader = createUploaderService();
|
|
105
103
|
return uploader.upload(uploadOptions);
|
|
106
104
|
};
|
|
107
105
|
|
|
108
106
|
/**
|
|
109
107
|
* Start TDD mode (convenience method)
|
|
110
108
|
*/
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
let startTDD = async (tddOptions = {}) => {
|
|
110
|
+
let tddService = createTDDServiceInstance();
|
|
113
111
|
return tddService.start(tddOptions);
|
|
114
112
|
};
|
|
115
113
|
return {
|
|
@@ -122,7 +120,6 @@ export function createVizzly(config = {}, options = {}) {
|
|
|
122
120
|
createTDDService: createTDDServiceInstance,
|
|
123
121
|
// Utilities
|
|
124
122
|
loadConfig: () => loadConfig(),
|
|
125
|
-
createLogger: loggerOptions => createComponentLogger('USER', loggerOptions),
|
|
126
123
|
// Config access
|
|
127
124
|
getConfig: () => ({
|
|
128
125
|
...resolvedConfig
|
|
@@ -151,13 +148,11 @@ export function createVizzly(config = {}, options = {}) {
|
|
|
151
148
|
export class VizzlySDK extends EventEmitter {
|
|
152
149
|
/**
|
|
153
150
|
* @param {import('../types').VizzlyConfig} config - Configuration
|
|
154
|
-
* @param {import('../utils/logger').Logger} logger - Logger instance
|
|
155
151
|
* @param {Object} services - Service instances
|
|
156
152
|
*/
|
|
157
|
-
constructor(config,
|
|
153
|
+
constructor(config, services) {
|
|
158
154
|
super();
|
|
159
155
|
this.config = config;
|
|
160
|
-
this.logger = logger;
|
|
161
156
|
this.services = services;
|
|
162
157
|
this.server = null;
|
|
163
158
|
this.currentBuildId = null;
|
|
@@ -172,7 +167,7 @@ export class VizzlySDK extends EventEmitter {
|
|
|
172
167
|
await this.server.stop();
|
|
173
168
|
this.server = null;
|
|
174
169
|
this.emit('server:stopped');
|
|
175
|
-
|
|
170
|
+
output.debug('Vizzly server stopped');
|
|
176
171
|
}
|
|
177
172
|
}
|
|
178
173
|
|
|
@@ -192,7 +187,7 @@ export class VizzlySDK extends EventEmitter {
|
|
|
192
187
|
*/
|
|
193
188
|
async start() {
|
|
194
189
|
if (this.server) {
|
|
195
|
-
|
|
190
|
+
output.warn('Server already running');
|
|
196
191
|
return {
|
|
197
192
|
port: this.config.server?.port || 3000,
|
|
198
193
|
url: `http://localhost:${this.config.server?.port || 3000}`
|
|
@@ -200,7 +195,7 @@ export class VizzlySDK extends EventEmitter {
|
|
|
200
195
|
}
|
|
201
196
|
|
|
202
197
|
// Create a simple build manager for screenshot collection
|
|
203
|
-
|
|
198
|
+
let buildManager = {
|
|
204
199
|
screenshots: new Map(),
|
|
205
200
|
currentBuildId: null,
|
|
206
201
|
async addScreenshot(buildId, screenshot) {
|
|
@@ -213,10 +208,10 @@ export class VizzlySDK extends EventEmitter {
|
|
|
213
208
|
return this.screenshots.get(buildId) || [];
|
|
214
209
|
}
|
|
215
210
|
};
|
|
216
|
-
this.server = new ScreenshotServer(this.config,
|
|
211
|
+
this.server = new ScreenshotServer(this.config, buildManager);
|
|
217
212
|
await this.server.start();
|
|
218
|
-
|
|
219
|
-
|
|
213
|
+
let port = this.config.server?.port || 3000;
|
|
214
|
+
let serverInfo = {
|
|
220
215
|
port,
|
|
221
216
|
url: `http://localhost:${port}`
|
|
222
217
|
};
|
|
@@ -240,15 +235,15 @@ export class VizzlySDK extends EventEmitter {
|
|
|
240
235
|
}
|
|
241
236
|
|
|
242
237
|
// Resolve Buffer or file path using shared utility
|
|
243
|
-
|
|
238
|
+
let buffer = resolveImageBuffer(imageBuffer, 'screenshot');
|
|
244
239
|
|
|
245
240
|
// Generate or use provided build ID
|
|
246
|
-
|
|
241
|
+
let buildId = options.buildId || this.currentBuildId || 'default';
|
|
247
242
|
this.currentBuildId = buildId;
|
|
248
243
|
|
|
249
244
|
// Convert Buffer to base64 for JSON transport
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
let imageBase64 = buffer.toString('base64');
|
|
246
|
+
let screenshotData = {
|
|
252
247
|
buildId,
|
|
253
248
|
name,
|
|
254
249
|
image: imageBase64,
|
|
@@ -256,9 +251,9 @@ export class VizzlySDK extends EventEmitter {
|
|
|
256
251
|
};
|
|
257
252
|
|
|
258
253
|
// POST to the local screenshot server
|
|
259
|
-
|
|
254
|
+
let serverUrl = `http://localhost:${this.config.server?.port || 3000}`;
|
|
260
255
|
try {
|
|
261
|
-
|
|
256
|
+
let response = await fetch(`${serverUrl}/screenshot`, {
|
|
262
257
|
method: 'POST',
|
|
263
258
|
headers: {
|
|
264
259
|
'Content-Type': 'application/json'
|
|
@@ -266,7 +261,7 @@ export class VizzlySDK extends EventEmitter {
|
|
|
266
261
|
body: JSON.stringify(screenshotData)
|
|
267
262
|
});
|
|
268
263
|
if (!response.ok) {
|
|
269
|
-
|
|
264
|
+
let errorData = await response.json().catch(() => ({
|
|
270
265
|
error: 'Unknown error'
|
|
271
266
|
}));
|
|
272
267
|
throw new VizzlyError(`Screenshot capture failed: ${errorData.error}`, 'SCREENSHOT_FAILED', {
|
|
@@ -280,7 +275,7 @@ export class VizzlySDK extends EventEmitter {
|
|
|
280
275
|
buildId,
|
|
281
276
|
options
|
|
282
277
|
});
|
|
283
|
-
|
|
278
|
+
output.debug(`Screenshot captured: ${name}`);
|
|
284
279
|
} catch (error) {
|
|
285
280
|
if (error instanceof VizzlyError) throw error;
|
|
286
281
|
throw new VizzlyError(`Failed to send screenshot to server: ${error.message}`, 'SCREENSHOT_TRANSPORT_ERROR', {
|
|
@@ -303,14 +298,12 @@ export class VizzlySDK extends EventEmitter {
|
|
|
303
298
|
apiKey: this.config.apiKey,
|
|
304
299
|
apiUrl: this.config.apiUrl,
|
|
305
300
|
upload: this.config.upload
|
|
306
|
-
}, {
|
|
307
|
-
logger: this.logger
|
|
308
301
|
});
|
|
309
302
|
}
|
|
310
303
|
|
|
311
304
|
// Get the screenshots directory from config or default
|
|
312
|
-
|
|
313
|
-
|
|
305
|
+
let screenshotsDir = options.screenshotsDir || this.config?.upload?.screenshotsDir || './screenshots';
|
|
306
|
+
let uploadOptions = {
|
|
314
307
|
screenshotsDir,
|
|
315
308
|
buildName: options.buildName || this.config.buildName,
|
|
316
309
|
branch: options.branch || this.config.branch,
|
|
@@ -326,7 +319,7 @@ export class VizzlySDK extends EventEmitter {
|
|
|
326
319
|
}
|
|
327
320
|
};
|
|
328
321
|
try {
|
|
329
|
-
|
|
322
|
+
let result = await this.services.uploader.upload(uploadOptions);
|
|
330
323
|
this.emit('upload:completed', result);
|
|
331
324
|
return result;
|
|
332
325
|
} catch (error) {
|
|
@@ -346,15 +339,13 @@ export class VizzlySDK extends EventEmitter {
|
|
|
346
339
|
async compare(name, imageBuffer) {
|
|
347
340
|
if (!this.services?.tddService) {
|
|
348
341
|
this.services = this.services || {};
|
|
349
|
-
this.services.tddService = createTDDService(this.config
|
|
350
|
-
logger: this.logger
|
|
351
|
-
});
|
|
342
|
+
this.services.tddService = createTDDService(this.config);
|
|
352
343
|
}
|
|
353
344
|
|
|
354
345
|
// Resolve Buffer or file path using shared utility
|
|
355
|
-
|
|
346
|
+
let buffer = resolveImageBuffer(imageBuffer, 'compare');
|
|
356
347
|
try {
|
|
357
|
-
|
|
348
|
+
let result = await this.services.tddService.compareScreenshot(name, buffer);
|
|
358
349
|
this.emit('comparison:completed', result);
|
|
359
350
|
return result;
|
|
360
351
|
} catch (error) {
|
|
@@ -369,7 +360,7 @@ export class VizzlySDK extends EventEmitter {
|
|
|
369
360
|
|
|
370
361
|
// Re-export key utilities and errors
|
|
371
362
|
export { loadConfig } from '../utils/config-loader.js';
|
|
372
|
-
export
|
|
363
|
+
export * as output from '../utils/output.js';
|
|
373
364
|
|
|
374
365
|
// Export service creators for advanced usage
|
|
375
366
|
export { createUploader } from '../services/uploader.js';
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
2
|
import { existsSync, readFileSync } from 'fs';
|
|
3
3
|
import { resolve } from 'path';
|
|
4
|
-
import
|
|
4
|
+
import * as output from '../../utils/output.js';
|
|
5
5
|
import { detectImageInputType } from '../../utils/image-input-detector.js';
|
|
6
|
-
const logger = createServiceLogger('API-HANDLER');
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* API Handler - Non-blocking screenshot upload
|
|
@@ -41,7 +40,7 @@ export const createApiHandler = apiService => {
|
|
|
41
40
|
let uploadPromises = [];
|
|
42
41
|
const handleScreenshot = async (buildId, name, image, properties = {}) => {
|
|
43
42
|
if (vizzlyDisabled) {
|
|
44
|
-
|
|
43
|
+
output.debug('upload', `${name} (disabled)`);
|
|
45
44
|
return {
|
|
46
45
|
statusCode: 200,
|
|
47
46
|
body: {
|
|
@@ -81,7 +80,6 @@ export const createApiHandler = apiService => {
|
|
|
81
80
|
}
|
|
82
81
|
try {
|
|
83
82
|
imageBuffer = readFileSync(filePath);
|
|
84
|
-
logger.debug(`Loaded screenshot from file: ${filePath}`);
|
|
85
83
|
} catch (error) {
|
|
86
84
|
return {
|
|
87
85
|
statusCode: 500,
|
|
@@ -117,10 +115,8 @@ export const createApiHandler = apiService => {
|
|
|
117
115
|
|
|
118
116
|
// Fire upload in background - DON'T AWAIT!
|
|
119
117
|
const uploadPromise = apiService.uploadScreenshot(buildId, name, imageBuffer, properties ?? {}).then(result => {
|
|
120
|
-
if (result.skipped) {
|
|
121
|
-
|
|
122
|
-
} else {
|
|
123
|
-
logger.debug(`Screenshot uploaded: ${name}`);
|
|
118
|
+
if (!result.skipped) {
|
|
119
|
+
output.debug('upload', name);
|
|
124
120
|
}
|
|
125
121
|
return {
|
|
126
122
|
success: true,
|
|
@@ -128,10 +124,11 @@ export const createApiHandler = apiService => {
|
|
|
128
124
|
result
|
|
129
125
|
};
|
|
130
126
|
}).catch(uploadError => {
|
|
131
|
-
|
|
127
|
+
output.debug('upload', `${name} failed`, {
|
|
128
|
+
error: uploadError.message
|
|
129
|
+
});
|
|
132
130
|
vizzlyDisabled = true;
|
|
133
|
-
|
|
134
|
-
logger.warn(disabledMessage);
|
|
131
|
+
output.warn('Vizzly disabled due to upload error - continuing tests without visual testing');
|
|
135
132
|
return {
|
|
136
133
|
success: false,
|
|
137
134
|
name,
|
|
@@ -160,14 +157,13 @@ export const createApiHandler = apiService => {
|
|
|
160
157
|
*/
|
|
161
158
|
const flush = async () => {
|
|
162
159
|
if (uploadPromises.length === 0) {
|
|
163
|
-
logger.debug('No uploads to flush');
|
|
164
160
|
return {
|
|
165
161
|
uploaded: 0,
|
|
166
162
|
failed: 0,
|
|
167
163
|
total: 0
|
|
168
164
|
};
|
|
169
165
|
}
|
|
170
|
-
|
|
166
|
+
output.debug('upload', `flushing ${uploadPromises.length} uploads`);
|
|
171
167
|
const results = await Promise.allSettled(uploadPromises);
|
|
172
168
|
let uploaded = 0;
|
|
173
169
|
let failed = 0;
|
|
@@ -178,7 +174,10 @@ export const createApiHandler = apiService => {
|
|
|
178
174
|
failed++;
|
|
179
175
|
}
|
|
180
176
|
});
|
|
181
|
-
|
|
177
|
+
output.debug('upload', 'flush complete', {
|
|
178
|
+
uploaded,
|
|
179
|
+
failed
|
|
180
|
+
});
|
|
182
181
|
|
|
183
182
|
// Clear promises array
|
|
184
183
|
uploadPromises = [];
|
|
@@ -192,7 +191,7 @@ export const createApiHandler = apiService => {
|
|
|
192
191
|
vizzlyDisabled = false;
|
|
193
192
|
screenshotCount = 0;
|
|
194
193
|
uploadPromises = [];
|
|
195
|
-
|
|
194
|
+
// Silent cleanup
|
|
196
195
|
};
|
|
197
196
|
return {
|
|
198
197
|
handleScreenshot,
|
|
@@ -2,14 +2,13 @@ import { Buffer } from 'buffer';
|
|
|
2
2
|
import { writeFileSync, readFileSync, existsSync } from 'fs';
|
|
3
3
|
import { join, resolve } from 'path';
|
|
4
4
|
import honeydiff from '@vizzly-testing/honeydiff';
|
|
5
|
-
import
|
|
5
|
+
import * as output from '../../utils/output.js';
|
|
6
6
|
import { TddService } from '../../services/tdd-service.js';
|
|
7
7
|
import { sanitizeScreenshotName, validateScreenshotProperties } from '../../utils/security.js';
|
|
8
8
|
import { detectImageInputType } from '../../utils/image-input-detector.js';
|
|
9
9
|
let {
|
|
10
10
|
getDimensionsSync
|
|
11
11
|
} = honeydiff;
|
|
12
|
-
const logger = createServiceLogger('TDD-HANDLER');
|
|
13
12
|
|
|
14
13
|
/**
|
|
15
14
|
* Group comparisons by screenshot name with variant structure
|
|
@@ -115,7 +114,7 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
115
114
|
const data = readFileSync(reportPath, 'utf8');
|
|
116
115
|
return JSON.parse(data);
|
|
117
116
|
} catch (error) {
|
|
118
|
-
|
|
117
|
+
output.error('Failed to read report data:', error);
|
|
119
118
|
return {
|
|
120
119
|
timestamp: Date.now(),
|
|
121
120
|
comparisons: [],
|
|
@@ -144,10 +143,8 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
144
143
|
const existingIndex = reportData.comparisons.findIndex(c => c.id === newComparison.id);
|
|
145
144
|
if (existingIndex >= 0) {
|
|
146
145
|
reportData.comparisons[existingIndex] = newComparison;
|
|
147
|
-
logger.debug(`Updated comparison for ${newComparison.name} (${newComparison.properties?.viewport_width}x${newComparison.properties?.viewport_height})`);
|
|
148
146
|
} else {
|
|
149
147
|
reportData.comparisons.push(newComparison);
|
|
150
|
-
logger.debug(`Added new comparison for ${newComparison.name} (${newComparison.properties?.viewport_width}x${newComparison.properties?.viewport_height})`);
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
// Generate grouped structure from flat comparisons
|
|
@@ -163,24 +160,23 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
163
160
|
errors: reportData.comparisons.filter(c => c.status === 'error').length
|
|
164
161
|
};
|
|
165
162
|
writeFileSync(reportPath, JSON.stringify(reportData, null, 2));
|
|
166
|
-
logger.debug('Report data saved with grouped structure');
|
|
167
163
|
} catch (error) {
|
|
168
|
-
|
|
164
|
+
output.error('Failed to update comparison:', error);
|
|
169
165
|
}
|
|
170
166
|
};
|
|
171
167
|
const initialize = async () => {
|
|
172
|
-
|
|
168
|
+
output.debug('tdd', 'setting up local comparison');
|
|
173
169
|
|
|
174
170
|
// In baseline update mode, skip all baseline loading/downloading
|
|
175
171
|
if (setBaseline) {
|
|
176
|
-
|
|
172
|
+
output.debug('tdd', 'baseline update mode');
|
|
177
173
|
return;
|
|
178
174
|
}
|
|
179
175
|
|
|
180
176
|
// Check if we have baseline override flags that should force a fresh download
|
|
181
177
|
const shouldForceDownload = (baselineBuild || baselineComparison) && config.apiKey;
|
|
182
178
|
if (shouldForceDownload) {
|
|
183
|
-
|
|
179
|
+
output.debug('tdd', 'downloading baselines from cloud');
|
|
184
180
|
await tddService.downloadBaselines(config.build?.environment || 'test', config.build?.branch || null, baselineBuild, baselineComparison);
|
|
185
181
|
return;
|
|
186
182
|
}
|
|
@@ -188,13 +184,13 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
188
184
|
if (!baseline) {
|
|
189
185
|
// Only download baselines if explicitly requested via baseline flags
|
|
190
186
|
if ((baselineBuild || baselineComparison) && config.apiKey) {
|
|
191
|
-
|
|
187
|
+
output.debug('tdd', 'downloading baselines from cloud');
|
|
192
188
|
await tddService.downloadBaselines(config.build?.environment || 'test', config.build?.branch || null, baselineBuild, baselineComparison);
|
|
193
189
|
} else {
|
|
194
|
-
|
|
190
|
+
output.debug('tdd', 'no baselines found, will create on first run');
|
|
195
191
|
}
|
|
196
192
|
} else {
|
|
197
|
-
|
|
193
|
+
output.debug('tdd', `using baseline: ${baseline.buildName}`);
|
|
198
194
|
}
|
|
199
195
|
};
|
|
200
196
|
const handleScreenshot = async (buildId, name, image, properties = {}) => {
|
|
@@ -275,7 +271,6 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
275
271
|
}
|
|
276
272
|
try {
|
|
277
273
|
imageBuffer = readFileSync(filePath);
|
|
278
|
-
logger.debug(`Loaded screenshot from file: ${filePath}`);
|
|
279
274
|
} catch (error) {
|
|
280
275
|
return {
|
|
281
276
|
statusCode: 500,
|
|
@@ -321,16 +316,16 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
321
316
|
if (!extractedProperties.viewport_height) {
|
|
322
317
|
extractedProperties.viewport_height = dimensions.height;
|
|
323
318
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
logger.debug(`Failed to auto-detect dimensions: ${err.message}`);
|
|
319
|
+
} catch {
|
|
320
|
+
// Dimensions will use defaults
|
|
327
321
|
}
|
|
328
322
|
}
|
|
329
323
|
|
|
330
324
|
// Use the sanitized name as-is (no modification with browser/viewport)
|
|
331
325
|
// Baseline matching uses signature logic (name + viewport_width + browser)
|
|
332
326
|
const comparison = await tddService.compareScreenshot(sanitizedName, imageBuffer, extractedProperties);
|
|
333
|
-
|
|
327
|
+
|
|
328
|
+
// Comparison tracked by tdd.js event handler
|
|
334
329
|
|
|
335
330
|
// Convert absolute file paths to web-accessible URLs
|
|
336
331
|
const convertPathToUrl = filePath => {
|
|
@@ -409,7 +404,8 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
409
404
|
}
|
|
410
405
|
};
|
|
411
406
|
}
|
|
412
|
-
|
|
407
|
+
|
|
408
|
+
// Debug output handled by tdd.js event handler
|
|
413
409
|
return {
|
|
414
410
|
statusCode: 200,
|
|
415
411
|
body: {
|
|
@@ -445,16 +441,16 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
445
441
|
diff: null
|
|
446
442
|
};
|
|
447
443
|
updateComparison(updatedComparison);
|
|
448
|
-
|
|
444
|
+
output.info(`Baseline accepted for comparison ${comparisonId}`);
|
|
449
445
|
return result;
|
|
450
446
|
} catch (error) {
|
|
451
|
-
|
|
447
|
+
output.error(`Failed to accept baseline for ${comparisonId}:`, error);
|
|
452
448
|
throw error;
|
|
453
449
|
}
|
|
454
450
|
};
|
|
455
451
|
const acceptAllBaselines = async () => {
|
|
456
452
|
try {
|
|
457
|
-
|
|
453
|
+
output.debug('tdd', 'accepting all baselines');
|
|
458
454
|
const reportData = readReportData();
|
|
459
455
|
let acceptedCount = 0;
|
|
460
456
|
|
|
@@ -475,18 +471,18 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
475
471
|
acceptedCount++;
|
|
476
472
|
}
|
|
477
473
|
}
|
|
478
|
-
|
|
474
|
+
output.info(`Accepted ${acceptedCount} baselines`);
|
|
479
475
|
return {
|
|
480
476
|
count: acceptedCount
|
|
481
477
|
};
|
|
482
478
|
} catch (error) {
|
|
483
|
-
|
|
479
|
+
output.error('Failed to accept all baselines:', error);
|
|
484
480
|
throw error;
|
|
485
481
|
}
|
|
486
482
|
};
|
|
487
483
|
const resetBaselines = async () => {
|
|
488
484
|
try {
|
|
489
|
-
|
|
485
|
+
output.debug('tdd', 'resetting baselines');
|
|
490
486
|
const reportData = readReportData();
|
|
491
487
|
let deletedBaselines = 0;
|
|
492
488
|
let deletedCurrents = 0;
|
|
@@ -504,9 +500,9 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
504
500
|
} = await import('fs');
|
|
505
501
|
unlinkSync(baselinePath);
|
|
506
502
|
deletedBaselines++;
|
|
507
|
-
|
|
503
|
+
// Silent deletion
|
|
508
504
|
} catch (error) {
|
|
509
|
-
|
|
505
|
+
output.warn(`Failed to delete baseline for ${comparison.name}: ${error.message}`);
|
|
510
506
|
}
|
|
511
507
|
}
|
|
512
508
|
}
|
|
@@ -521,9 +517,9 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
521
517
|
} = await import('fs');
|
|
522
518
|
unlinkSync(currentPath);
|
|
523
519
|
deletedCurrents++;
|
|
524
|
-
|
|
520
|
+
// Silent deletion
|
|
525
521
|
} catch (error) {
|
|
526
|
-
|
|
522
|
+
output.warn(`Failed to delete current screenshot for ${comparison.name}: ${error.message}`);
|
|
527
523
|
}
|
|
528
524
|
}
|
|
529
525
|
}
|
|
@@ -538,9 +534,9 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
538
534
|
} = await import('fs');
|
|
539
535
|
unlinkSync(diffPath);
|
|
540
536
|
deletedDiffs++;
|
|
541
|
-
|
|
537
|
+
output.debug(`Deleted diff for ${comparison.name}`);
|
|
542
538
|
} catch (error) {
|
|
543
|
-
|
|
539
|
+
output.warn(`Failed to delete diff for ${comparison.name}: ${error.message}`);
|
|
544
540
|
}
|
|
545
541
|
}
|
|
546
542
|
}
|
|
@@ -554,9 +550,9 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
554
550
|
unlinkSync
|
|
555
551
|
} = await import('fs');
|
|
556
552
|
unlinkSync(metadataPath);
|
|
557
|
-
|
|
553
|
+
output.debug('Deleted baseline metadata');
|
|
558
554
|
} catch (error) {
|
|
559
|
-
|
|
555
|
+
output.warn(`Failed to delete baseline metadata: ${error.message}`);
|
|
560
556
|
}
|
|
561
557
|
}
|
|
562
558
|
|
|
@@ -574,7 +570,7 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
574
570
|
}
|
|
575
571
|
};
|
|
576
572
|
writeFileSync(reportPath, JSON.stringify(freshReportData, null, 2));
|
|
577
|
-
|
|
573
|
+
output.info(`Baselines reset - ${deletedBaselines} baselines deleted, ${deletedCurrents} current screenshots deleted, ${deletedDiffs} diffs deleted`);
|
|
578
574
|
return {
|
|
579
575
|
success: true,
|
|
580
576
|
deletedBaselines,
|
|
@@ -582,13 +578,12 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
582
578
|
deletedDiffs
|
|
583
579
|
};
|
|
584
580
|
} catch (error) {
|
|
585
|
-
|
|
581
|
+
output.error('Failed to reset baselines:', error);
|
|
586
582
|
throw error;
|
|
587
583
|
}
|
|
588
584
|
};
|
|
589
585
|
const cleanup = () => {
|
|
590
586
|
// Report data is persisted to file, no in-memory cleanup needed
|
|
591
|
-
logger.debug('TDD handler cleanup completed');
|
|
592
587
|
};
|
|
593
588
|
return {
|
|
594
589
|
initialize,
|
|
@@ -597,6 +592,8 @@ export const createTddHandler = (config, workingDir, baselineBuild, baselineComp
|
|
|
597
592
|
acceptBaseline,
|
|
598
593
|
acceptAllBaselines,
|
|
599
594
|
resetBaselines,
|
|
600
|
-
cleanup
|
|
595
|
+
cleanup,
|
|
596
|
+
// Expose tddService for baseline download operations
|
|
597
|
+
tddService
|
|
601
598
|
};
|
|
602
599
|
};
|