@vizzly-testing/cli 0.1.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/LICENSE +21 -0
- package/README.md +363 -0
- package/bin/vizzly.js +3 -0
- package/dist/cli.js +104 -0
- package/dist/client/index.js +237 -0
- package/dist/commands/doctor.js +158 -0
- package/dist/commands/init.js +102 -0
- package/dist/commands/run.js +224 -0
- package/dist/commands/status.js +164 -0
- package/dist/commands/tdd.js +212 -0
- package/dist/commands/upload.js +181 -0
- package/dist/container/index.js +184 -0
- package/dist/errors/vizzly-error.js +149 -0
- package/dist/index.js +31 -0
- package/dist/screenshot-wrapper.js +68 -0
- package/dist/sdk/index.js +364 -0
- package/dist/server/index.js +522 -0
- package/dist/services/api-service.js +215 -0
- package/dist/services/base-service.js +154 -0
- package/dist/services/build-manager.js +214 -0
- package/dist/services/screenshot-server.js +96 -0
- package/dist/services/server-manager.js +61 -0
- package/dist/services/service-utils.js +171 -0
- package/dist/services/tdd-service.js +444 -0
- package/dist/services/test-runner.js +210 -0
- package/dist/services/uploader.js +413 -0
- package/dist/types/cli.d.ts +2 -0
- package/dist/types/client/index.d.ts +76 -0
- package/dist/types/commands/doctor.d.ts +11 -0
- package/dist/types/commands/init.d.ts +14 -0
- package/dist/types/commands/run.d.ts +13 -0
- package/dist/types/commands/status.d.ts +13 -0
- package/dist/types/commands/tdd.d.ts +13 -0
- package/dist/types/commands/upload.d.ts +13 -0
- package/dist/types/container/index.d.ts +61 -0
- package/dist/types/errors/vizzly-error.d.ts +75 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.js +153 -0
- package/dist/types/screenshot-wrapper.d.ts +27 -0
- package/dist/types/sdk/index.d.ts +108 -0
- package/dist/types/server/index.d.ts +38 -0
- package/dist/types/services/api-service.d.ts +77 -0
- package/dist/types/services/base-service.d.ts +72 -0
- package/dist/types/services/build-manager.d.ts +68 -0
- package/dist/types/services/screenshot-server.d.ts +10 -0
- package/dist/types/services/server-manager.d.ts +8 -0
- package/dist/types/services/service-utils.d.ts +45 -0
- package/dist/types/services/tdd-service.d.ts +55 -0
- package/dist/types/services/test-runner.d.ts +25 -0
- package/dist/types/services/uploader.d.ts +34 -0
- package/dist/types/types/index.d.ts +373 -0
- package/dist/types/utils/colors.d.ts +12 -0
- package/dist/types/utils/config-helpers.d.ts +6 -0
- package/dist/types/utils/config-loader.d.ts +22 -0
- package/dist/types/utils/console-ui.d.ts +61 -0
- package/dist/types/utils/diagnostics.d.ts +69 -0
- package/dist/types/utils/environment-config.d.ts +54 -0
- package/dist/types/utils/environment.d.ts +36 -0
- package/dist/types/utils/error-messages.d.ts +42 -0
- package/dist/types/utils/fetch-utils.d.ts +1 -0
- package/dist/types/utils/framework-detector.d.ts +5 -0
- package/dist/types/utils/git.d.ts +44 -0
- package/dist/types/utils/help.d.ts +11 -0
- package/dist/types/utils/image-comparison.d.ts +42 -0
- package/dist/types/utils/logger-factory.d.ts +26 -0
- package/dist/types/utils/logger.d.ts +79 -0
- package/dist/types/utils/package-info.d.ts +15 -0
- package/dist/types/utils/package.d.ts +1 -0
- package/dist/types/utils/project-detection.d.ts +19 -0
- package/dist/types/utils/ui-helpers.d.ts +23 -0
- package/dist/utils/colors.js +66 -0
- package/dist/utils/config-helpers.js +8 -0
- package/dist/utils/config-loader.js +120 -0
- package/dist/utils/console-ui.js +226 -0
- package/dist/utils/diagnostics.js +184 -0
- package/dist/utils/environment-config.js +93 -0
- package/dist/utils/environment.js +109 -0
- package/dist/utils/error-messages.js +34 -0
- package/dist/utils/fetch-utils.js +9 -0
- package/dist/utils/framework-detector.js +40 -0
- package/dist/utils/git.js +226 -0
- package/dist/utils/help.js +66 -0
- package/dist/utils/image-comparison.js +172 -0
- package/dist/utils/logger-factory.js +76 -0
- package/dist/utils/logger.js +231 -0
- package/dist/utils/package-info.js +38 -0
- package/dist/utils/package.js +9 -0
- package/dist/utils/project-detection.js +145 -0
- package/dist/utils/ui-helpers.js +86 -0
- package/package.json +103 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vizzly SDK - Full API for custom integrations
|
|
3
|
+
*
|
|
4
|
+
* This is the comprehensive SDK for building custom Vizzly integrations.
|
|
5
|
+
* For simple test runner usage, use @vizzly-testing/cli/client instead.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @module @vizzly-testing/cli/sdk
|
|
10
|
+
* @description Full SDK for custom integrations and advanced usage
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EventEmitter } from 'events';
|
|
14
|
+
import { createUploader } from '../services/uploader.js';
|
|
15
|
+
import { createTDDService } from '../services/tdd-service.js';
|
|
16
|
+
import { ScreenshotServer } from '../services/screenshot-server.js';
|
|
17
|
+
import { loadConfig } from '../utils/config-loader.js';
|
|
18
|
+
import { createComponentLogger } from '../utils/logger-factory.js';
|
|
19
|
+
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a new Vizzly instance with custom configuration
|
|
23
|
+
*
|
|
24
|
+
* @param {import('../types').VizzlyConfig} [config] - Configuration options
|
|
25
|
+
* @returns {Promise<VizzlySDK>} Configured Vizzly SDK instance
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Create with custom config
|
|
29
|
+
* import { createVizzly } from '@vizzly-testing/cli/sdk';
|
|
30
|
+
*
|
|
31
|
+
* const vizzly = await createVizzly({
|
|
32
|
+
* apiKey: process.env.VIZZLY_TOKEN,
|
|
33
|
+
* apiUrl: 'https://vizzly.dev',
|
|
34
|
+
* server: {
|
|
35
|
+
* port: 3003,
|
|
36
|
+
* enabled: true
|
|
37
|
+
* }
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* // Start the server
|
|
41
|
+
* await vizzly.start();
|
|
42
|
+
*
|
|
43
|
+
* // Take screenshots
|
|
44
|
+
* const screenshot = await getScreenshotSomehow();
|
|
45
|
+
* await vizzly.screenshot('my-test', screenshot);
|
|
46
|
+
*
|
|
47
|
+
* // Upload results
|
|
48
|
+
* const result = await vizzly.upload();
|
|
49
|
+
* console.log(`Build URL: ${result.url}`);
|
|
50
|
+
*
|
|
51
|
+
* // Cleanup
|
|
52
|
+
* await vizzly.stop();
|
|
53
|
+
*/
|
|
54
|
+
export function createVizzly(config = {}, options = {}) {
|
|
55
|
+
const logger = options.logger || createComponentLogger('SDK', {
|
|
56
|
+
level: options.logLevel || 'info',
|
|
57
|
+
verbose: options.verbose || false
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Merge with loaded config
|
|
61
|
+
const resolvedConfig = {
|
|
62
|
+
...config
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Initialize SDK with config loading
|
|
67
|
+
*/
|
|
68
|
+
const init = async () => {
|
|
69
|
+
const fileConfig = await loadConfig();
|
|
70
|
+
Object.assign(resolvedConfig, fileConfig, config); // CLI config takes precedence
|
|
71
|
+
return resolvedConfig;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Create uploader service
|
|
76
|
+
*/
|
|
77
|
+
const createUploaderService = (uploaderOptions = {}) => {
|
|
78
|
+
return createUploader({
|
|
79
|
+
apiKey: resolvedConfig.apiKey,
|
|
80
|
+
apiUrl: resolvedConfig.apiUrl
|
|
81
|
+
}, {
|
|
82
|
+
...options,
|
|
83
|
+
...uploaderOptions,
|
|
84
|
+
logger
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create TDD service
|
|
90
|
+
*/
|
|
91
|
+
const createTDDServiceInstance = (tddOptions = {}) => {
|
|
92
|
+
return createTDDService(resolvedConfig, {
|
|
93
|
+
...options,
|
|
94
|
+
...tddOptions,
|
|
95
|
+
logger
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Upload screenshots (convenience method)
|
|
101
|
+
*/
|
|
102
|
+
const upload = async uploadOptions => {
|
|
103
|
+
const uploader = createUploaderService();
|
|
104
|
+
return uploader.upload(uploadOptions);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Start TDD mode (convenience method)
|
|
109
|
+
*/
|
|
110
|
+
const startTDD = async (tddOptions = {}) => {
|
|
111
|
+
const tddService = createTDDServiceInstance();
|
|
112
|
+
return tddService.start(tddOptions);
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
// Core methods
|
|
116
|
+
init,
|
|
117
|
+
upload,
|
|
118
|
+
startTDD,
|
|
119
|
+
// Service factories
|
|
120
|
+
createUploader: createUploaderService,
|
|
121
|
+
createTDDService: createTDDServiceInstance,
|
|
122
|
+
// Utilities
|
|
123
|
+
loadConfig: () => loadConfig(),
|
|
124
|
+
createLogger: loggerOptions => createComponentLogger('USER', loggerOptions),
|
|
125
|
+
// Config access
|
|
126
|
+
getConfig: () => ({
|
|
127
|
+
...resolvedConfig
|
|
128
|
+
}),
|
|
129
|
+
updateConfig: newConfig => Object.assign(resolvedConfig, newConfig)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @typedef {Object} VizzlySDK
|
|
135
|
+
* @property {Function} start - Start the Vizzly server
|
|
136
|
+
* @property {Function} stop - Stop the Vizzly server
|
|
137
|
+
* @property {Function} screenshot - Capture a screenshot
|
|
138
|
+
* @property {Function} upload - Upload screenshots to Vizzly
|
|
139
|
+
* @property {Function} compare - Run local comparison (TDD mode)
|
|
140
|
+
* @property {Function} getConfig - Get current configuration
|
|
141
|
+
* @property {Function} on - Subscribe to events
|
|
142
|
+
* @property {Function} off - Unsubscribe from events
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* VizzlySDK class implementation
|
|
147
|
+
* @class
|
|
148
|
+
* @extends {EventEmitter}
|
|
149
|
+
*/
|
|
150
|
+
export class VizzlySDK extends EventEmitter {
|
|
151
|
+
/**
|
|
152
|
+
* @param {import('../types').VizzlyConfig} config - Configuration
|
|
153
|
+
* @param {import('../utils/logger').Logger} logger - Logger instance
|
|
154
|
+
* @param {Object} services - Service instances
|
|
155
|
+
*/
|
|
156
|
+
constructor(config, logger, services) {
|
|
157
|
+
super();
|
|
158
|
+
this.config = config;
|
|
159
|
+
this.logger = logger;
|
|
160
|
+
this.services = services;
|
|
161
|
+
this.server = null;
|
|
162
|
+
this.currentBuildId = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Stop the Vizzly server
|
|
167
|
+
* @returns {Promise<void>}
|
|
168
|
+
*/
|
|
169
|
+
async stop() {
|
|
170
|
+
if (this.server) {
|
|
171
|
+
await this.server.stop();
|
|
172
|
+
this.server = null;
|
|
173
|
+
this.emit('server:stopped');
|
|
174
|
+
this.logger.info('Vizzly server stopped');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get current configuration
|
|
180
|
+
* @returns {Object} Current config
|
|
181
|
+
*/
|
|
182
|
+
getConfig() {
|
|
183
|
+
return {
|
|
184
|
+
...this.config
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Start the Vizzly server
|
|
190
|
+
* @returns {Promise<{port: number, url: string}>} Server information
|
|
191
|
+
*/
|
|
192
|
+
async start() {
|
|
193
|
+
if (this.server) {
|
|
194
|
+
this.logger.warn('Server already running');
|
|
195
|
+
return {
|
|
196
|
+
port: this.config.server?.port || 3000,
|
|
197
|
+
url: `http://localhost:${this.config.server?.port || 3000}`
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Create a simple build manager for screenshot collection
|
|
202
|
+
const buildManager = {
|
|
203
|
+
screenshots: new Map(),
|
|
204
|
+
currentBuildId: null,
|
|
205
|
+
async addScreenshot(buildId, screenshot) {
|
|
206
|
+
if (!this.screenshots.has(buildId)) {
|
|
207
|
+
this.screenshots.set(buildId, []);
|
|
208
|
+
}
|
|
209
|
+
this.screenshots.get(buildId).push(screenshot);
|
|
210
|
+
},
|
|
211
|
+
getScreenshots(buildId) {
|
|
212
|
+
return this.screenshots.get(buildId) || [];
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
this.server = new ScreenshotServer(this.config, this.logger, buildManager);
|
|
216
|
+
await this.server.start();
|
|
217
|
+
const port = this.config.server?.port || 3000;
|
|
218
|
+
const serverInfo = {
|
|
219
|
+
port,
|
|
220
|
+
url: `http://localhost:${port}`
|
|
221
|
+
};
|
|
222
|
+
this.emit('server:started', serverInfo);
|
|
223
|
+
return serverInfo;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Capture a screenshot
|
|
228
|
+
* @param {string} name - Screenshot name
|
|
229
|
+
* @param {Buffer} imageBuffer - Image data
|
|
230
|
+
* @param {import('../types').ScreenshotOptions} [options] - Options
|
|
231
|
+
* @returns {Promise<void>}
|
|
232
|
+
*/
|
|
233
|
+
async screenshot(name, imageBuffer, options = {}) {
|
|
234
|
+
if (!this.server || !this.server.isRunning()) {
|
|
235
|
+
throw new VizzlyError('Server not running. Call start() first.', 'SERVER_NOT_RUNNING');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Generate or use provided build ID
|
|
239
|
+
const buildId = options.buildId || this.currentBuildId || 'default';
|
|
240
|
+
this.currentBuildId = buildId;
|
|
241
|
+
|
|
242
|
+
// Convert Buffer to base64 for JSON transport
|
|
243
|
+
const imageBase64 = imageBuffer.toString('base64');
|
|
244
|
+
const screenshotData = {
|
|
245
|
+
buildId,
|
|
246
|
+
name,
|
|
247
|
+
image: imageBase64,
|
|
248
|
+
properties: options.properties || {}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// POST to the local screenshot server
|
|
252
|
+
const serverUrl = `http://localhost:${this.config.server?.port || 3000}`;
|
|
253
|
+
try {
|
|
254
|
+
const response = await fetch(`${serverUrl}/screenshot`, {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
headers: {
|
|
257
|
+
'Content-Type': 'application/json'
|
|
258
|
+
},
|
|
259
|
+
body: JSON.stringify(screenshotData)
|
|
260
|
+
});
|
|
261
|
+
if (!response.ok) {
|
|
262
|
+
const errorData = await response.json().catch(() => ({
|
|
263
|
+
error: 'Unknown error'
|
|
264
|
+
}));
|
|
265
|
+
throw new VizzlyError(`Screenshot capture failed: ${errorData.error}`, 'SCREENSHOT_FAILED', {
|
|
266
|
+
name,
|
|
267
|
+
buildId,
|
|
268
|
+
status: response.status
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
this.emit('screenshot:captured', {
|
|
272
|
+
name,
|
|
273
|
+
buildId,
|
|
274
|
+
options
|
|
275
|
+
});
|
|
276
|
+
this.logger.debug(`Screenshot captured: ${name}`);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error instanceof VizzlyError) throw error;
|
|
279
|
+
throw new VizzlyError(`Failed to send screenshot to server: ${error.message}`, 'SCREENSHOT_TRANSPORT_ERROR', {
|
|
280
|
+
name,
|
|
281
|
+
buildId,
|
|
282
|
+
originalError: error.message
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Upload all captured screenshots
|
|
289
|
+
* @param {import('../types').UploadOptions} [options] - Upload options
|
|
290
|
+
* @returns {Promise<import('../types').UploadResult>} Upload result
|
|
291
|
+
*/
|
|
292
|
+
async upload(options = {}) {
|
|
293
|
+
if (!this.services?.uploader) {
|
|
294
|
+
this.services = this.services || {};
|
|
295
|
+
this.services.uploader = createUploader({
|
|
296
|
+
apiKey: this.config.apiKey,
|
|
297
|
+
apiUrl: this.config.apiUrl,
|
|
298
|
+
upload: this.config.upload
|
|
299
|
+
}, {
|
|
300
|
+
logger: this.logger
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Get the screenshots directory from config or default
|
|
305
|
+
const screenshotsDir = options.screenshotsDir || this.config?.upload?.screenshotsDir || './screenshots';
|
|
306
|
+
const uploadOptions = {
|
|
307
|
+
screenshotsDir,
|
|
308
|
+
buildName: options.buildName || this.config.buildName,
|
|
309
|
+
branch: options.branch || this.config.branch,
|
|
310
|
+
commit: options.commit || this.config.commit,
|
|
311
|
+
message: options.message || this.config.message,
|
|
312
|
+
environment: options.environment || this.config.environment || 'production',
|
|
313
|
+
threshold: options.threshold || this.config.threshold,
|
|
314
|
+
onProgress: progress => {
|
|
315
|
+
this.emit('upload:progress', progress);
|
|
316
|
+
if (options.onProgress) {
|
|
317
|
+
options.onProgress(progress);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
try {
|
|
322
|
+
const result = await this.services.uploader.upload(uploadOptions);
|
|
323
|
+
this.emit('upload:completed', result);
|
|
324
|
+
return result;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
this.emit('upload:failed', error);
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Run local comparison in TDD mode
|
|
333
|
+
* @param {string} name - Screenshot name
|
|
334
|
+
* @param {Buffer} imageBuffer - Current image
|
|
335
|
+
* @returns {Promise<import('../types').ComparisonResult>} Comparison result
|
|
336
|
+
*/
|
|
337
|
+
async compare(name, imageBuffer) {
|
|
338
|
+
if (!this.services?.tddService) {
|
|
339
|
+
this.services = this.services || {};
|
|
340
|
+
this.services.tddService = createTDDService(this.config, {
|
|
341
|
+
logger: this.logger
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
const result = await this.services.tddService.compareScreenshot(name, imageBuffer);
|
|
346
|
+
this.emit('comparison:completed', result);
|
|
347
|
+
return result;
|
|
348
|
+
} catch (error) {
|
|
349
|
+
this.emit('comparison:failed', {
|
|
350
|
+
name,
|
|
351
|
+
error
|
|
352
|
+
});
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Re-export key utilities and errors
|
|
359
|
+
export { loadConfig } from '../utils/config-loader.js';
|
|
360
|
+
export { createLogger } from '../utils/logger.js';
|
|
361
|
+
|
|
362
|
+
// Export service creators for advanced usage
|
|
363
|
+
export { createUploader } from '../services/uploader.js';
|
|
364
|
+
export { createTDDService } from '../services/tdd-service.js';
|