@vizzly-testing/cli 0.13.4 → 0.15.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/cli.js +68 -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 +78 -32
- 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/fullscreen-viewer.d.ts +13 -0
- package/dist/types/reporter/src/components/comparison/screenshot-display.d.ts +16 -0
- package/dist/types/reporter/src/components/comparison/screenshot-list.d.ts +9 -0
- package/dist/types/reporter/src/components/comparison/variant-selector.d.ts +1 -1
- 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 +5 -0
- package/dist/types/reporter/src/components/views/comparisons-view.d.ts +5 -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 +22 -22
- 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/commands/run.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from '../utils/config-loader.js';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import * as output from '../utils/output.js';
|
|
3
|
+
import { createServices } from '../services/index.js';
|
|
4
4
|
import { detectBranch, detectCommit, detectCommitMessage, detectPullRequestNumber, generateBuildNameWithGit } from '../utils/git.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -10,8 +10,7 @@ import { detectBranch, detectCommit, detectCommitMessage, detectPullRequestNumbe
|
|
|
10
10
|
* @param {Object} globalOptions - Global CLI options
|
|
11
11
|
*/
|
|
12
12
|
export async function runCommand(testCommand, options = {}, globalOptions = {}) {
|
|
13
|
-
|
|
14
|
-
const ui = new ConsoleUI({
|
|
13
|
+
output.configure({
|
|
15
14
|
json: globalOptions.json,
|
|
16
15
|
verbose: globalOptions.verbose,
|
|
17
16
|
color: !globalOptions.noColor
|
|
@@ -22,8 +21,8 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
22
21
|
let isTddMode = false;
|
|
23
22
|
|
|
24
23
|
// Ensure cleanup on exit
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
let cleanup = async () => {
|
|
25
|
+
output.cleanup();
|
|
27
26
|
|
|
28
27
|
// Cancel test runner (kills process and stops server)
|
|
29
28
|
if (testRunner) {
|
|
@@ -37,42 +36,37 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
37
36
|
// Finalize build if we have one
|
|
38
37
|
if (testRunner && buildId) {
|
|
39
38
|
try {
|
|
40
|
-
|
|
39
|
+
let executionTime = Date.now() - (startTime || Date.now());
|
|
41
40
|
await testRunner.finalizeBuild(buildId, isTddMode, false, executionTime);
|
|
42
41
|
} catch {
|
|
43
42
|
// Silent fail on cleanup
|
|
44
43
|
}
|
|
45
44
|
}
|
|
46
45
|
};
|
|
47
|
-
|
|
46
|
+
let sigintHandler = async () => {
|
|
48
47
|
await cleanup();
|
|
49
48
|
process.exit(1);
|
|
50
49
|
};
|
|
51
|
-
|
|
50
|
+
let exitHandler = () => output.cleanup();
|
|
52
51
|
process.on('SIGINT', sigintHandler);
|
|
53
52
|
process.on('exit', exitHandler);
|
|
54
53
|
try {
|
|
55
54
|
// Load configuration with CLI overrides
|
|
56
|
-
|
|
55
|
+
let allOptions = {
|
|
57
56
|
...globalOptions,
|
|
58
57
|
...options
|
|
59
58
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (process.env.DEBUG_CONFIG) {
|
|
69
|
-
console.log('[RUN] Config after loadConfig:', {
|
|
70
|
-
hasApiKey: !!config.apiKey,
|
|
71
|
-
apiKeyPrefix: config.apiKey ? config.apiKey.substring(0, 8) + '***' : 'NONE'
|
|
72
|
-
});
|
|
73
|
-
}
|
|
59
|
+
output.debug('[RUN] Loading config', {
|
|
60
|
+
hasToken: !!allOptions.token
|
|
61
|
+
});
|
|
62
|
+
let config = await loadConfig(globalOptions.config, allOptions);
|
|
63
|
+
output.debug('[RUN] Config loaded', {
|
|
64
|
+
hasApiKey: !!config.apiKey,
|
|
65
|
+
apiKeyPrefix: config.apiKey ? config.apiKey.substring(0, 8) + '***' : 'NONE'
|
|
66
|
+
});
|
|
74
67
|
if (globalOptions.verbose) {
|
|
75
|
-
|
|
68
|
+
output.info('Token check:');
|
|
69
|
+
output.debug('Token details', {
|
|
76
70
|
hasApiKey: !!config.apiKey,
|
|
77
71
|
apiKeyType: typeof config.apiKey,
|
|
78
72
|
apiKeyPrefix: typeof config.apiKey === 'string' && config.apiKey ? config.apiKey.substring(0, 10) + '...' : 'none',
|
|
@@ -83,18 +77,19 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
83
77
|
|
|
84
78
|
// Validate API token (unless --allow-no-token is set)
|
|
85
79
|
if (!config.apiKey && !config.allowNoToken) {
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
output.error('API token required. Use --token, set VIZZLY_TOKEN environment variable, or use --allow-no-token to run without uploading');
|
|
81
|
+
process.exit(1);
|
|
88
82
|
}
|
|
89
83
|
|
|
90
84
|
// Collect git metadata and build info
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
let branch = await detectBranch(options.branch);
|
|
86
|
+
let commit = await detectCommit(options.commit);
|
|
87
|
+
let message = options.message || (await detectCommitMessage());
|
|
88
|
+
let buildName = await generateBuildNameWithGit(options.buildName);
|
|
89
|
+
let pullRequestNumber = detectPullRequestNumber();
|
|
96
90
|
if (globalOptions.verbose) {
|
|
97
|
-
|
|
91
|
+
output.info('Configuration loaded');
|
|
92
|
+
output.debug('Config details', {
|
|
98
93
|
testCommand,
|
|
99
94
|
port: config.server.port,
|
|
100
95
|
timeout: config.server.timeout,
|
|
@@ -108,77 +103,68 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
108
103
|
}
|
|
109
104
|
|
|
110
105
|
// Create service container and get test runner service
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
output.startSpinner('Initializing test runner...');
|
|
107
|
+
let configWithVerbose = {
|
|
113
108
|
...config,
|
|
114
109
|
verbose: globalOptions.verbose,
|
|
115
110
|
uploadAll: options.uploadAll || false
|
|
116
111
|
};
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
const command = 'run';
|
|
126
|
-
const container = await createServiceContainer(configWithVerbose, command);
|
|
127
|
-
testRunner = await container.get('testRunner'); // Assign to outer scope variable
|
|
128
|
-
ui.stopSpinner();
|
|
112
|
+
output.debug('[RUN] Creating services', {
|
|
113
|
+
hasApiKey: !!configWithVerbose.apiKey
|
|
114
|
+
});
|
|
115
|
+
let services = createServices(configWithVerbose, 'run');
|
|
116
|
+
testRunner = services.testRunner;
|
|
117
|
+
output.stopSpinner();
|
|
129
118
|
|
|
130
119
|
// Track build URL for display
|
|
131
120
|
let buildUrl = null;
|
|
132
121
|
|
|
133
122
|
// Set up event handlers
|
|
134
123
|
testRunner.on('progress', progressData => {
|
|
135
|
-
|
|
124
|
+
let {
|
|
136
125
|
message: progressMessage
|
|
137
126
|
} = progressData;
|
|
138
|
-
|
|
127
|
+
output.progress(progressMessage || 'Running tests...');
|
|
139
128
|
});
|
|
140
|
-
testRunner.on('test-output',
|
|
129
|
+
testRunner.on('test-output', data => {
|
|
141
130
|
// In non-JSON mode, show test output directly
|
|
142
131
|
if (!globalOptions.json) {
|
|
143
|
-
|
|
144
|
-
|
|
132
|
+
output.stopSpinner();
|
|
133
|
+
output.print(data.data);
|
|
145
134
|
}
|
|
146
135
|
});
|
|
147
136
|
testRunner.on('server-ready', serverInfo => {
|
|
148
137
|
if (globalOptions.verbose) {
|
|
149
|
-
|
|
150
|
-
|
|
138
|
+
output.info(`Screenshot server running on port ${serverInfo.port}`);
|
|
139
|
+
output.debug('Server details', serverInfo);
|
|
151
140
|
}
|
|
152
141
|
});
|
|
153
142
|
testRunner.on('screenshot-captured', screenshotInfo => {
|
|
154
|
-
|
|
155
|
-
ui.info(`Vizzly: Screenshot captured - ${screenshotInfo.name}`);
|
|
143
|
+
output.info(`Vizzly: Screenshot captured - ${screenshotInfo.name}`);
|
|
156
144
|
});
|
|
157
145
|
testRunner.on('build-created', buildInfo => {
|
|
158
146
|
buildUrl = buildInfo.url;
|
|
159
147
|
buildId = buildInfo.buildId;
|
|
160
|
-
// Debug: Log build creation details
|
|
161
148
|
if (globalOptions.verbose) {
|
|
162
|
-
|
|
149
|
+
output.info(`Build created: ${buildInfo.buildId} - ${buildInfo.name}`);
|
|
163
150
|
}
|
|
164
|
-
// Use UI for consistent formatting
|
|
165
151
|
if (buildUrl) {
|
|
166
|
-
|
|
152
|
+
output.info(`Vizzly: ${buildUrl}`);
|
|
167
153
|
}
|
|
168
154
|
});
|
|
169
155
|
testRunner.on('build-failed', buildError => {
|
|
170
|
-
|
|
156
|
+
output.error('Failed to create build', buildError);
|
|
171
157
|
});
|
|
172
158
|
testRunner.on('error', error => {
|
|
173
|
-
|
|
174
|
-
|
|
159
|
+
output.stopSpinner();
|
|
160
|
+
output.error('Test runner error occurred', error);
|
|
175
161
|
});
|
|
176
162
|
testRunner.on('build-finalize-failed', errorInfo => {
|
|
177
|
-
|
|
163
|
+
output.warn(`Failed to finalize build ${errorInfo.buildId}: ${errorInfo.error}`);
|
|
178
164
|
});
|
|
179
165
|
|
|
180
166
|
// Prepare run options
|
|
181
|
-
|
|
167
|
+
let runOptions = {
|
|
182
168
|
testCommand,
|
|
183
169
|
port: config.server.port,
|
|
184
170
|
timeout: config.server.timeout,
|
|
@@ -197,7 +183,7 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
197
183
|
};
|
|
198
184
|
|
|
199
185
|
// Start test run
|
|
200
|
-
|
|
186
|
+
output.info('Starting test execution...');
|
|
201
187
|
startTime = Date.now();
|
|
202
188
|
isTddMode = runOptions.tdd || false;
|
|
203
189
|
let result;
|
|
@@ -208,32 +194,32 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
208
194
|
if (result.buildId) {
|
|
209
195
|
buildId = result.buildId;
|
|
210
196
|
}
|
|
211
|
-
|
|
197
|
+
output.success('Test run completed successfully');
|
|
212
198
|
|
|
213
199
|
// Show Vizzly summary
|
|
214
200
|
if (result.buildId) {
|
|
215
|
-
|
|
201
|
+
output.print(`🐻 Vizzly: Captured ${result.screenshotsCaptured} screenshots in build ${result.buildId}`);
|
|
216
202
|
if (result.url) {
|
|
217
|
-
|
|
203
|
+
output.print(`🔗 Vizzly: View results at ${result.url}`);
|
|
218
204
|
}
|
|
219
205
|
}
|
|
220
206
|
} catch (error) {
|
|
221
207
|
// Test execution failed - build should already be finalized by test runner
|
|
222
|
-
|
|
208
|
+
output.stopSpinner();
|
|
223
209
|
|
|
224
210
|
// Check if it's a test command failure (as opposed to setup failure)
|
|
225
211
|
if (error.code === 'TEST_COMMAND_FAILED' || error.code === 'TEST_COMMAND_INTERRUPTED') {
|
|
226
212
|
// Extract exit code from error message if available
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
213
|
+
let exitCodeMatch = error.message.match(/exited with code (\d+)/);
|
|
214
|
+
let exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 1;
|
|
215
|
+
output.error('Test run failed');
|
|
230
216
|
return {
|
|
231
217
|
success: false,
|
|
232
218
|
exitCode
|
|
233
219
|
};
|
|
234
220
|
} else {
|
|
235
221
|
// Setup or other error - VizzlyError.getUserMessage() provides context
|
|
236
|
-
|
|
222
|
+
output.error('Test run failed', error);
|
|
237
223
|
return {
|
|
238
224
|
success: false,
|
|
239
225
|
exitCode: 1
|
|
@@ -245,16 +231,17 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
245
231
|
if (result.buildId) {
|
|
246
232
|
// Wait for build completion if requested
|
|
247
233
|
if (runOptions.wait) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
234
|
+
output.info('Waiting for build completion...');
|
|
235
|
+
output.startSpinner('Processing comparisons...');
|
|
236
|
+
let {
|
|
237
|
+
uploader
|
|
238
|
+
} = services;
|
|
239
|
+
let buildResult = await uploader.waitForBuild(result.buildId);
|
|
240
|
+
output.success('Build processing completed');
|
|
253
241
|
|
|
254
242
|
// Exit with appropriate code based on comparison results
|
|
255
243
|
if (buildResult.failedComparisons > 0) {
|
|
256
|
-
|
|
257
|
-
// Return error status without calling process.exit in tests
|
|
244
|
+
output.error(`${buildResult.failedComparisons} visual comparisons failed`);
|
|
258
245
|
return {
|
|
259
246
|
success: false,
|
|
260
247
|
exitCode: 1
|
|
@@ -262,9 +249,9 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
262
249
|
}
|
|
263
250
|
}
|
|
264
251
|
}
|
|
265
|
-
|
|
252
|
+
output.cleanup();
|
|
266
253
|
} catch (error) {
|
|
267
|
-
|
|
254
|
+
output.stopSpinner();
|
|
268
255
|
|
|
269
256
|
// Provide more context about where the error occurred
|
|
270
257
|
let errorContext = 'Test run failed';
|
|
@@ -275,7 +262,8 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
275
262
|
} else if (error.message && error.message.includes('server')) {
|
|
276
263
|
errorContext = 'Server startup failed';
|
|
277
264
|
}
|
|
278
|
-
|
|
265
|
+
output.error(errorContext, error);
|
|
266
|
+
process.exit(1);
|
|
279
267
|
} finally {
|
|
280
268
|
// Remove event listeners to prevent memory leaks
|
|
281
269
|
process.removeListener('SIGINT', sigintHandler);
|
|
@@ -289,30 +277,30 @@ export async function runCommand(testCommand, options = {}, globalOptions = {})
|
|
|
289
277
|
* @param {Object} options - Command options
|
|
290
278
|
*/
|
|
291
279
|
export function validateRunOptions(testCommand, options) {
|
|
292
|
-
|
|
280
|
+
let errors = [];
|
|
293
281
|
if (!testCommand || testCommand.trim() === '') {
|
|
294
282
|
errors.push('Test command is required');
|
|
295
283
|
}
|
|
296
284
|
if (options.port) {
|
|
297
|
-
|
|
285
|
+
let port = parseInt(options.port, 10);
|
|
298
286
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
299
287
|
errors.push('Port must be a valid number between 1 and 65535');
|
|
300
288
|
}
|
|
301
289
|
}
|
|
302
290
|
if (options.timeout) {
|
|
303
|
-
|
|
291
|
+
let timeout = parseInt(options.timeout, 10);
|
|
304
292
|
if (isNaN(timeout) || timeout < 1000) {
|
|
305
293
|
errors.push('Timeout must be at least 1000 milliseconds');
|
|
306
294
|
}
|
|
307
295
|
}
|
|
308
296
|
if (options.batchSize !== undefined) {
|
|
309
|
-
|
|
297
|
+
let n = parseInt(options.batchSize, 10);
|
|
310
298
|
if (!Number.isFinite(n) || n <= 0) {
|
|
311
299
|
errors.push('Batch size must be a positive integer');
|
|
312
300
|
}
|
|
313
301
|
}
|
|
314
302
|
if (options.uploadTimeout !== undefined) {
|
|
315
|
-
|
|
303
|
+
let n = parseInt(options.uploadTimeout, 10);
|
|
316
304
|
if (!Number.isFinite(n) || n <= 0) {
|
|
317
305
|
errors.push('Upload timeout must be a positive integer (milliseconds)');
|
|
318
306
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from '../utils/config-loader.js';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import * as output from '../utils/output.js';
|
|
3
|
+
import { createServices } from '../services/index.js';
|
|
4
4
|
import { getApiUrl } from '../utils/environment-config.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -10,84 +10,82 @@ import { getApiUrl } from '../utils/environment-config.js';
|
|
|
10
10
|
* @param {Object} globalOptions - Global CLI options
|
|
11
11
|
*/
|
|
12
12
|
export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
13
|
-
|
|
14
|
-
const ui = new ConsoleUI({
|
|
13
|
+
output.configure({
|
|
15
14
|
json: globalOptions.json,
|
|
16
15
|
verbose: globalOptions.verbose,
|
|
17
16
|
color: !globalOptions.noColor
|
|
18
17
|
});
|
|
19
|
-
|
|
20
|
-
// Note: ConsoleUI handles cleanup via global process listeners
|
|
21
|
-
|
|
22
18
|
try {
|
|
23
|
-
|
|
19
|
+
output.info(`Checking status for build: ${buildId}`);
|
|
24
20
|
|
|
25
21
|
// Load configuration with CLI overrides
|
|
26
|
-
|
|
22
|
+
let allOptions = {
|
|
27
23
|
...globalOptions,
|
|
28
24
|
...options
|
|
29
25
|
};
|
|
30
|
-
|
|
26
|
+
let config = await loadConfig(globalOptions.config, allOptions);
|
|
31
27
|
|
|
32
28
|
// Validate API token
|
|
33
29
|
if (!config.apiKey) {
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
output.error('API token required. Use --token or set VIZZLY_TOKEN environment variable');
|
|
31
|
+
process.exit(1);
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
// Get API service
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
output.startSpinner('Fetching build status...');
|
|
36
|
+
let services = createServices(config, 'status');
|
|
37
|
+
let {
|
|
38
|
+
apiService
|
|
39
|
+
} = services;
|
|
42
40
|
|
|
43
41
|
// Get build details via unified ApiService
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
let buildStatus = await apiService.getBuild(buildId);
|
|
43
|
+
output.stopSpinner();
|
|
46
44
|
|
|
47
45
|
// Extract build data from API response
|
|
48
|
-
|
|
46
|
+
let build = buildStatus.build || buildStatus;
|
|
49
47
|
|
|
50
48
|
// Display build summary
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
output.success(`Build: ${build.name || build.id}`);
|
|
50
|
+
output.info(`Status: ${build.status.toUpperCase()}`);
|
|
51
|
+
output.info(`Environment: ${build.environment}`);
|
|
54
52
|
if (build.branch) {
|
|
55
|
-
|
|
53
|
+
output.info(`Branch: ${build.branch}`);
|
|
56
54
|
}
|
|
57
55
|
if (build.commit_sha) {
|
|
58
|
-
|
|
56
|
+
output.info(`Commit: ${build.commit_sha.substring(0, 8)} - ${build.commit_message || 'No message'}`);
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
// Show screenshot and comparison stats
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
output.info(`Screenshots: ${build.screenshot_count || 0} total`);
|
|
61
|
+
output.info(`Comparisons: ${build.total_comparisons || 0} total (${build.new_comparisons || 0} new, ${build.changed_comparisons || 0} changed, ${build.identical_comparisons || 0} identical)`);
|
|
64
62
|
if (build.approval_status) {
|
|
65
|
-
|
|
63
|
+
output.info(`Approval Status: ${build.approval_status}`);
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
// Show timing information
|
|
69
67
|
if (build.created_at) {
|
|
70
|
-
|
|
68
|
+
output.info(`Created: ${new Date(build.created_at).toLocaleString()}`);
|
|
71
69
|
}
|
|
72
70
|
if (build.completed_at) {
|
|
73
|
-
|
|
71
|
+
output.info(`Completed: ${new Date(build.completed_at).toLocaleString()}`);
|
|
74
72
|
} else if (build.status !== 'completed' && build.status !== 'failed') {
|
|
75
|
-
|
|
73
|
+
output.info(`Started: ${new Date(build.started_at || build.created_at).toLocaleString()}`);
|
|
76
74
|
}
|
|
77
75
|
if (build.execution_time_ms) {
|
|
78
|
-
|
|
76
|
+
output.info(`Execution Time: ${Math.round(build.execution_time_ms / 1000)}s`);
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
// Show build URL if we can construct it
|
|
82
|
-
|
|
80
|
+
let baseUrl = config.baseUrl || getApiUrl();
|
|
83
81
|
if (baseUrl && build.project_id) {
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
let buildUrl = baseUrl.replace('/api', '') + `/projects/${build.project_id}/builds/${build.id}`;
|
|
83
|
+
output.info(`View Build: ${buildUrl}`);
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
// Output JSON data for --json mode
|
|
89
87
|
if (globalOptions.json) {
|
|
90
|
-
|
|
88
|
+
let statusData = {
|
|
91
89
|
buildId: build.id,
|
|
92
90
|
status: build.status,
|
|
93
91
|
name: build.name,
|
|
@@ -108,45 +106,46 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
|
108
106
|
isBaseline: build.is_baseline,
|
|
109
107
|
userAgent: build.user_agent
|
|
110
108
|
};
|
|
111
|
-
|
|
109
|
+
output.data(statusData);
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
// Show additional info in verbose mode
|
|
115
113
|
if (globalOptions.verbose) {
|
|
116
|
-
|
|
114
|
+
output.info('\n--- Additional Details ---');
|
|
117
115
|
if (build.approved_screenshots > 0 || build.rejected_screenshots > 0 || build.pending_screenshots > 0) {
|
|
118
|
-
|
|
116
|
+
output.info(`Screenshot Approvals: ${build.approved_screenshots || 0} approved, ${build.rejected_screenshots || 0} rejected, ${build.pending_screenshots || 0} pending`);
|
|
119
117
|
}
|
|
120
118
|
if (build.avg_diff_percentage !== null) {
|
|
121
|
-
|
|
119
|
+
output.info(`Average Diff: ${(build.avg_diff_percentage * 100).toFixed(2)}%`);
|
|
122
120
|
}
|
|
123
121
|
if (build.github_pull_request_number) {
|
|
124
|
-
|
|
122
|
+
output.info(`GitHub PR: #${build.github_pull_request_number}`);
|
|
125
123
|
}
|
|
126
124
|
if (build.is_baseline) {
|
|
127
|
-
|
|
125
|
+
output.info('This build is marked as a baseline');
|
|
128
126
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
127
|
+
output.info(`User Agent: ${build.user_agent || 'Unknown'}`);
|
|
128
|
+
output.info(`Build ID: ${build.id}`);
|
|
129
|
+
output.info(`Project ID: ${build.project_id}`);
|
|
132
130
|
}
|
|
133
131
|
|
|
134
132
|
// Show progress if build is still processing
|
|
135
133
|
if (build.status === 'processing' || build.status === 'pending') {
|
|
136
|
-
|
|
134
|
+
let totalJobs = build.completed_jobs + build.failed_jobs + build.processing_screenshots;
|
|
137
135
|
if (totalJobs > 0) {
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
let progress = (build.completed_jobs + build.failed_jobs) / totalJobs;
|
|
137
|
+
output.info(`Progress: ${Math.round(progress * 100)}% complete`);
|
|
140
138
|
}
|
|
141
139
|
}
|
|
142
|
-
|
|
140
|
+
output.cleanup();
|
|
143
141
|
|
|
144
142
|
// Exit with appropriate code based on build status
|
|
145
143
|
if (build.status === 'failed' || build.failed_jobs > 0) {
|
|
146
144
|
process.exit(1);
|
|
147
145
|
}
|
|
148
146
|
} catch (error) {
|
|
149
|
-
|
|
147
|
+
output.error('Failed to get build status', error);
|
|
148
|
+
process.exit(1);
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
151
|
|
|
@@ -156,7 +155,7 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}) {
|
|
|
156
155
|
* @param {Object} options - Command options
|
|
157
156
|
*/
|
|
158
157
|
export function validateStatusOptions(buildId) {
|
|
159
|
-
|
|
158
|
+
let errors = [];
|
|
160
159
|
if (!buildId || buildId.trim() === '') {
|
|
161
160
|
errors.push('Build ID is required');
|
|
162
161
|
}
|