@vizzly-testing/cli 0.20.0 → 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/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 -250
- 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/uploader/core.js +396 -0
- package/dist/uploader/index.js +11 -0
- package/dist/uploader/operations.js +412 -0
- 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 -1437
package/dist/commands/tdd.js
CHANGED
|
@@ -1,43 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* TDD command implementation
|
|
3
|
+
* Uses functional operations directly - no class wrappers needed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { spawn as defaultSpawn } from 'node:child_process';
|
|
7
|
+
import { createBuild as defaultCreateApiBuild, createApiClient as defaultCreateApiClient, finalizeBuild as defaultFinalizeApiBuild, getBuild as defaultGetBuild } from '../api/index.js';
|
|
8
|
+
import { VizzlyError } from '../errors/vizzly-error.js';
|
|
9
|
+
import { createServerManager as defaultCreateServerManager } from '../server-manager/index.js';
|
|
10
|
+
import { createBuildObject as defaultCreateBuildObject } from '../services/build-manager.js';
|
|
11
|
+
import { initializeDaemon as defaultInitializeDaemon, runTests as defaultRunTests } from '../test-runner/index.js';
|
|
12
|
+
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
13
|
+
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit } from '../utils/git.js';
|
|
14
|
+
import * as defaultOutput from '../utils/output.js';
|
|
5
15
|
|
|
6
16
|
/**
|
|
7
17
|
* TDD command implementation
|
|
8
18
|
* @param {string} testCommand - Test command to execute
|
|
9
19
|
* @param {Object} options - Command options
|
|
10
20
|
* @param {Object} globalOptions - Global CLI options
|
|
21
|
+
* @param {Object} deps - Dependencies for testing
|
|
11
22
|
* @returns {Promise<{result: Object, cleanup: Function}>} Result and cleanup function
|
|
12
23
|
*/
|
|
13
|
-
export async function tddCommand(testCommand, options = {}, globalOptions = {}) {
|
|
24
|
+
export async function tddCommand(testCommand, options = {}, globalOptions = {}, deps = {}) {
|
|
25
|
+
let {
|
|
26
|
+
loadConfig = defaultLoadConfig,
|
|
27
|
+
createApiClient = defaultCreateApiClient,
|
|
28
|
+
createApiBuild = defaultCreateApiBuild,
|
|
29
|
+
finalizeApiBuild = defaultFinalizeApiBuild,
|
|
30
|
+
getBuild = defaultGetBuild,
|
|
31
|
+
createServerManager = defaultCreateServerManager,
|
|
32
|
+
createBuildObject = defaultCreateBuildObject,
|
|
33
|
+
initializeDaemon = defaultInitializeDaemon,
|
|
34
|
+
runTests = defaultRunTests,
|
|
35
|
+
detectBranch = defaultDetectBranch,
|
|
36
|
+
detectCommit = defaultDetectCommit,
|
|
37
|
+
spawn = defaultSpawn,
|
|
38
|
+
output = defaultOutput
|
|
39
|
+
} = deps;
|
|
14
40
|
output.configure({
|
|
15
41
|
json: globalOptions.json,
|
|
16
42
|
verbose: globalOptions.verbose,
|
|
17
43
|
color: !globalOptions.noColor
|
|
18
44
|
});
|
|
19
|
-
let
|
|
45
|
+
let serverManager = null;
|
|
46
|
+
let testProcess = null;
|
|
20
47
|
let isCleanedUp = false;
|
|
21
48
|
|
|
22
49
|
// Create cleanup function that can be called by the caller
|
|
23
|
-
|
|
50
|
+
let cleanup = async () => {
|
|
24
51
|
if (isCleanedUp) return;
|
|
25
52
|
isCleanedUp = true;
|
|
26
53
|
output.cleanup();
|
|
27
|
-
if (
|
|
28
|
-
|
|
54
|
+
if (testProcess && !testProcess.killed) {
|
|
55
|
+
testProcess.kill('SIGKILL');
|
|
56
|
+
}
|
|
57
|
+
if (serverManager) {
|
|
58
|
+
await serverManager.stop();
|
|
29
59
|
}
|
|
30
60
|
};
|
|
31
61
|
try {
|
|
32
62
|
// Load configuration with CLI overrides
|
|
33
|
-
|
|
63
|
+
let allOptions = {
|
|
34
64
|
...globalOptions,
|
|
35
65
|
...options
|
|
36
66
|
};
|
|
37
|
-
|
|
67
|
+
let config = await loadConfig(globalOptions.config, allOptions);
|
|
38
68
|
|
|
39
69
|
// Dev mode works locally by default - only needs token for baseline download
|
|
40
|
-
|
|
70
|
+
let needsToken = options.baselineBuild || options.baselineComparison;
|
|
41
71
|
if (!config.apiKey && needsToken) {
|
|
42
72
|
throw new Error('API token required when using --baseline-build or --baseline-comparison flags');
|
|
43
73
|
}
|
|
@@ -46,12 +76,12 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {})
|
|
|
46
76
|
config.allowNoToken = true;
|
|
47
77
|
|
|
48
78
|
// Collect git metadata
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
let branch = await detectBranch(options.branch);
|
|
80
|
+
let commit = await detectCommit(options.commit);
|
|
51
81
|
|
|
52
82
|
// Show header (skip in daemon mode)
|
|
53
83
|
if (!options.daemon) {
|
|
54
|
-
|
|
84
|
+
let mode = config.apiKey ? 'local' : 'local';
|
|
55
85
|
output.header('tdd', mode);
|
|
56
86
|
|
|
57
87
|
// Show config in verbose mode
|
|
@@ -62,81 +92,53 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {})
|
|
|
62
92
|
});
|
|
63
93
|
}
|
|
64
94
|
|
|
65
|
-
// Create
|
|
95
|
+
// Create functional dependencies
|
|
66
96
|
output.startSpinner('Initializing TDD server...');
|
|
67
|
-
|
|
97
|
+
let configWithVerbose = {
|
|
68
98
|
...config,
|
|
69
99
|
verbose: globalOptions.verbose
|
|
70
100
|
};
|
|
71
|
-
const services = createServices(configWithVerbose, 'tdd');
|
|
72
|
-
testRunner = services.testRunner;
|
|
73
|
-
output.stopSpinner();
|
|
74
101
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
testRunner.on('test-output', data => {
|
|
83
|
-
// In non-JSON mode, show test output directly
|
|
84
|
-
if (!globalOptions.json) {
|
|
85
|
-
output.stopSpinner();
|
|
86
|
-
output.print(data.data);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
testRunner.on('server-ready', serverInfo => {
|
|
90
|
-
// Only show in non-daemon mode (daemon shows its own startup message)
|
|
91
|
-
if (!options.daemon) {
|
|
92
|
-
output.debug('server', `listening on :${serverInfo.port}`);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
testRunner.on('screenshot-captured', screenshotInfo => {
|
|
96
|
-
output.debug('capture', screenshotInfo.name);
|
|
97
|
-
});
|
|
98
|
-
testRunner.on('comparison-result', comparisonInfo => {
|
|
99
|
-
const {
|
|
100
|
-
name,
|
|
101
|
-
status,
|
|
102
|
-
pixelDifference
|
|
103
|
-
} = comparisonInfo;
|
|
104
|
-
if (status === 'passed') {
|
|
105
|
-
output.debug('compare', `${name} passed`);
|
|
106
|
-
} else if (status === 'failed') {
|
|
107
|
-
output.warn(`${name}: ${pixelDifference}% difference`);
|
|
108
|
-
} else if (status === 'new') {
|
|
109
|
-
output.debug('compare', `${name} (new baseline)`);
|
|
102
|
+
// Create server manager (functional object)
|
|
103
|
+
serverManager = createServerManager(configWithVerbose, {});
|
|
104
|
+
|
|
105
|
+
// Create build manager (functional object that provides the interface runTests expects)
|
|
106
|
+
let buildManager = {
|
|
107
|
+
async createBuild(buildOptions) {
|
|
108
|
+
return createBuildObject(buildOptions);
|
|
110
109
|
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
const runOptions = {
|
|
110
|
+
};
|
|
111
|
+
output.stopSpinner();
|
|
112
|
+
let runOptions = {
|
|
116
113
|
testCommand,
|
|
117
114
|
port: config.server.port,
|
|
118
115
|
timeout: config.server.timeout,
|
|
119
116
|
tdd: true,
|
|
120
117
|
daemon: options.daemon || false,
|
|
121
|
-
// Daemon mode flag
|
|
122
118
|
setBaseline: options.setBaseline || false,
|
|
123
|
-
// Pass through baseline update mode
|
|
124
119
|
branch,
|
|
125
120
|
commit,
|
|
126
121
|
environment: config.build.environment,
|
|
127
122
|
threshold: config.comparison.threshold,
|
|
128
123
|
allowNoToken: config.allowNoToken || false,
|
|
129
|
-
// Pass through the allow-no-token setting
|
|
130
124
|
baselineBuildId: config.baselineBuildId,
|
|
131
125
|
baselineComparisonId: config.baselineComparisonId,
|
|
132
|
-
wait: false
|
|
126
|
+
wait: false
|
|
133
127
|
};
|
|
134
128
|
|
|
135
129
|
// In daemon mode, just start the server without running tests
|
|
136
130
|
if (options.daemon) {
|
|
137
|
-
await
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
await initializeDaemon({
|
|
132
|
+
initOptions: runOptions,
|
|
133
|
+
deps: {
|
|
134
|
+
serverManager,
|
|
135
|
+
createError: (msg, code) => new VizzlyError(msg, code),
|
|
136
|
+
output,
|
|
137
|
+
onServerReady: data => {
|
|
138
|
+
output.debug('server', `listening on :${data.port}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
140
142
|
return {
|
|
141
143
|
result: {
|
|
142
144
|
success: true,
|
|
@@ -149,19 +151,46 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {})
|
|
|
149
151
|
|
|
150
152
|
// Normal dev mode - run tests
|
|
151
153
|
output.debug('run', testCommand);
|
|
152
|
-
|
|
154
|
+
let runResult = await runTests({
|
|
155
|
+
runOptions,
|
|
156
|
+
config: configWithVerbose,
|
|
157
|
+
deps: {
|
|
158
|
+
serverManager,
|
|
159
|
+
buildManager,
|
|
160
|
+
spawn: (command, spawnOptions) => {
|
|
161
|
+
let proc = spawn(command, spawnOptions);
|
|
162
|
+
testProcess = proc;
|
|
163
|
+
return proc;
|
|
164
|
+
},
|
|
165
|
+
createApiClient,
|
|
166
|
+
createApiBuild,
|
|
167
|
+
getBuild,
|
|
168
|
+
finalizeApiBuild,
|
|
169
|
+
createError: (msg, code) => new VizzlyError(msg, code),
|
|
170
|
+
output,
|
|
171
|
+
onBuildCreated: data => {
|
|
172
|
+
output.debug('build', `created ${data.buildId?.substring(0, 8)}`);
|
|
173
|
+
},
|
|
174
|
+
onServerReady: data => {
|
|
175
|
+
output.debug('server', `listening on :${data.port}`);
|
|
176
|
+
},
|
|
177
|
+
onFinalizeFailed: data => {
|
|
178
|
+
output.warn(`Failed to finalize build: ${data.error}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
153
182
|
|
|
154
183
|
// Show summary
|
|
155
|
-
|
|
184
|
+
let {
|
|
156
185
|
screenshotsCaptured,
|
|
157
186
|
comparisons
|
|
158
187
|
} = runResult;
|
|
159
188
|
|
|
160
189
|
// Determine success based on comparison results
|
|
161
|
-
|
|
190
|
+
let hasFailures = runResult.failed || runResult.comparisons?.some(c => c.status === 'failed');
|
|
162
191
|
if (comparisons && comparisons.length > 0) {
|
|
163
|
-
|
|
164
|
-
|
|
192
|
+
let passed = comparisons.filter(c => c.status === 'passed').length;
|
|
193
|
+
let failed = comparisons.filter(c => c.status === 'failed').length;
|
|
165
194
|
if (hasFailures) {
|
|
166
195
|
output.error(`${failed} visual difference${failed !== 1 ? 's' : ''} detected`);
|
|
167
196
|
output.info(`Check .vizzly/diffs/ for diff images`);
|
|
@@ -171,8 +200,6 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {})
|
|
|
171
200
|
} else {
|
|
172
201
|
output.result(`${screenshotsCaptured} screenshot${screenshotsCaptured !== 1 ? 's' : ''}`);
|
|
173
202
|
}
|
|
174
|
-
|
|
175
|
-
// Return result and cleanup function
|
|
176
203
|
return {
|
|
177
204
|
result: {
|
|
178
205
|
success: !hasFailures,
|
|
@@ -200,24 +227,24 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {})
|
|
|
200
227
|
* @param {Object} options - Command options
|
|
201
228
|
*/
|
|
202
229
|
export function validateTddOptions(testCommand, options) {
|
|
203
|
-
|
|
230
|
+
let errors = [];
|
|
204
231
|
if (!testCommand || testCommand.trim() === '') {
|
|
205
232
|
errors.push('Test command is required');
|
|
206
233
|
}
|
|
207
234
|
if (options.port) {
|
|
208
|
-
|
|
235
|
+
let port = parseInt(options.port, 10);
|
|
209
236
|
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
210
237
|
errors.push('Port must be a valid number between 1 and 65535');
|
|
211
238
|
}
|
|
212
239
|
}
|
|
213
240
|
if (options.timeout) {
|
|
214
|
-
|
|
241
|
+
let timeout = parseInt(options.timeout, 10);
|
|
215
242
|
if (Number.isNaN(timeout) || timeout < 1000) {
|
|
216
243
|
errors.push('Timeout must be at least 1000 milliseconds');
|
|
217
244
|
}
|
|
218
245
|
}
|
|
219
246
|
if (options.threshold !== undefined) {
|
|
220
|
-
|
|
247
|
+
let threshold = parseFloat(options.threshold);
|
|
221
248
|
if (Number.isNaN(threshold) || threshold < 0) {
|
|
222
249
|
errors.push('Threshold must be a non-negative number (CIEDE2000 Delta E)');
|
|
223
250
|
}
|
package/dist/commands/upload.js
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { loadConfig } from '../utils/config-loader.js';
|
|
4
|
-
import { detectBranch, detectCommit, detectCommitMessage, detectPullRequestNumber, generateBuildNameWithGit } from '../utils/git.js';
|
|
5
|
-
import * as
|
|
1
|
+
import { createApiClient as defaultCreateApiClient, finalizeBuild as defaultFinalizeBuild, getTokenContext as defaultGetTokenContext } from '../api/index.js';
|
|
2
|
+
import { createUploader as defaultCreateUploader } from '../services/uploader.js';
|
|
3
|
+
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
4
|
+
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
5
|
+
import * as defaultOutput from '../utils/output.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Construct proper build URL with org/project context
|
|
9
9
|
* @param {string} buildId - Build ID
|
|
10
10
|
* @param {string} apiUrl - API base URL
|
|
11
11
|
* @param {string} apiToken - API token
|
|
12
|
+
* @param {Object} deps - Dependencies
|
|
12
13
|
* @returns {Promise<string>} Proper build URL
|
|
13
14
|
*/
|
|
14
|
-
async function constructBuildUrl(buildId, apiUrl, apiToken) {
|
|
15
|
+
export async function constructBuildUrl(buildId, apiUrl, apiToken, deps = {}) {
|
|
16
|
+
let {
|
|
17
|
+
createApiClient = defaultCreateApiClient,
|
|
18
|
+
getTokenContext = defaultGetTokenContext,
|
|
19
|
+
output = defaultOutput
|
|
20
|
+
} = deps;
|
|
15
21
|
try {
|
|
16
|
-
|
|
22
|
+
let client = createApiClient({
|
|
17
23
|
baseUrl: apiUrl,
|
|
18
24
|
token: apiToken,
|
|
19
25
|
command: 'upload'
|
|
20
26
|
});
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
let tokenContext = await getTokenContext(client);
|
|
28
|
+
let baseUrl = apiUrl.replace(/\/api.*$/, '');
|
|
23
29
|
if (tokenContext.organization?.slug && tokenContext.project?.slug) {
|
|
24
30
|
return `${baseUrl}/${tokenContext.organization.slug}/${tokenContext.project.slug}/builds/${buildId}`;
|
|
25
31
|
}
|
|
@@ -31,7 +37,7 @@ async function constructBuildUrl(buildId, apiUrl, apiToken) {
|
|
|
31
37
|
}
|
|
32
38
|
|
|
33
39
|
// Fallback URL construction
|
|
34
|
-
|
|
40
|
+
let baseUrl = apiUrl.replace(/\/api.*$/, '');
|
|
35
41
|
return `${baseUrl}/builds/${buildId}`;
|
|
36
42
|
}
|
|
37
43
|
|
|
@@ -40,8 +46,23 @@ async function constructBuildUrl(buildId, apiUrl, apiToken) {
|
|
|
40
46
|
* @param {string} screenshotsPath - Path to screenshots
|
|
41
47
|
* @param {Object} options - Command options
|
|
42
48
|
* @param {Object} globalOptions - Global CLI options
|
|
49
|
+
* @param {Object} deps - Dependencies for testing
|
|
43
50
|
*/
|
|
44
|
-
export async function uploadCommand(screenshotsPath, options = {}, globalOptions = {}) {
|
|
51
|
+
export async function uploadCommand(screenshotsPath, options = {}, globalOptions = {}, deps = {}) {
|
|
52
|
+
let {
|
|
53
|
+
loadConfig = defaultLoadConfig,
|
|
54
|
+
createApiClient = defaultCreateApiClient,
|
|
55
|
+
finalizeBuild = defaultFinalizeBuild,
|
|
56
|
+
createUploader = defaultCreateUploader,
|
|
57
|
+
detectBranch = defaultDetectBranch,
|
|
58
|
+
detectCommit = defaultDetectCommit,
|
|
59
|
+
detectCommitMessage = defaultDetectCommitMessage,
|
|
60
|
+
detectPullRequestNumber = defaultDetectPullRequestNumber,
|
|
61
|
+
generateBuildNameWithGit = defaultGenerateBuildNameWithGit,
|
|
62
|
+
output = defaultOutput,
|
|
63
|
+
exit = code => process.exit(code),
|
|
64
|
+
buildUrlConstructor = constructBuildUrl
|
|
65
|
+
} = deps;
|
|
45
66
|
output.configure({
|
|
46
67
|
json: globalOptions.json,
|
|
47
68
|
verbose: globalOptions.verbose,
|
|
@@ -63,7 +84,11 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
63
84
|
// Validate API token
|
|
64
85
|
if (!config.apiKey) {
|
|
65
86
|
output.error('API token required. Use --token or set VIZZLY_TOKEN environment variable');
|
|
66
|
-
|
|
87
|
+
exit(1);
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
reason: 'no-api-key'
|
|
91
|
+
};
|
|
67
92
|
}
|
|
68
93
|
|
|
69
94
|
// Collect git metadata if not provided
|
|
@@ -83,10 +108,12 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
83
108
|
});
|
|
84
109
|
}
|
|
85
110
|
|
|
86
|
-
//
|
|
111
|
+
// Create uploader
|
|
87
112
|
output.startSpinner('Initializing uploader...');
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
let uploader = createUploader({
|
|
114
|
+
...config,
|
|
115
|
+
command: 'upload'
|
|
116
|
+
});
|
|
90
117
|
|
|
91
118
|
// Prepare upload options with progress callback
|
|
92
119
|
const uploadOptions = {
|
|
@@ -135,13 +162,13 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
135
162
|
if (result.buildId) {
|
|
136
163
|
output.progress('Finalizing build...');
|
|
137
164
|
try {
|
|
138
|
-
|
|
165
|
+
let client = createApiClient({
|
|
139
166
|
baseUrl: config.apiUrl,
|
|
140
167
|
token: config.apiKey,
|
|
141
168
|
command: 'upload'
|
|
142
169
|
});
|
|
143
|
-
|
|
144
|
-
await
|
|
170
|
+
let executionTime = Date.now() - uploadStartTime;
|
|
171
|
+
await finalizeBuild(client, result.buildId, true, executionTime);
|
|
145
172
|
} catch (error) {
|
|
146
173
|
output.warn(`Failed to finalize build: ${error.message}`);
|
|
147
174
|
}
|
|
@@ -152,7 +179,7 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
152
179
|
if (result.buildId) {
|
|
153
180
|
output.info(`🐻 Vizzly: Uploaded ${result.stats.uploaded} of ${result.stats.total} screenshots to build ${result.buildId}`);
|
|
154
181
|
// Use API-provided URL or construct proper URL with org/project context
|
|
155
|
-
|
|
182
|
+
let buildUrl = result.url || (await buildUrlConstructor(result.buildId, config.apiUrl, config.apiKey, deps));
|
|
156
183
|
output.info(`🔗 Vizzly: View results at ${buildUrl}`);
|
|
157
184
|
}
|
|
158
185
|
|
|
@@ -170,29 +197,37 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
170
197
|
output.success(`All ${buildResult.passedComparisons} visual comparisons passed`);
|
|
171
198
|
}
|
|
172
199
|
// Use API-provided URL or construct proper URL with org/project context
|
|
173
|
-
|
|
174
|
-
output.info(`🔗 Vizzly: View results at ${
|
|
200
|
+
let waitBuildUrl = buildResult.url || (await buildUrlConstructor(result.buildId, config.apiUrl, config.apiKey, deps));
|
|
201
|
+
output.info(`🔗 Vizzly: View results at ${waitBuildUrl}`);
|
|
175
202
|
}
|
|
176
203
|
output.cleanup();
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
result
|
|
207
|
+
};
|
|
177
208
|
} catch (error) {
|
|
178
209
|
// Mark build as failed if we have a buildId and config
|
|
179
210
|
if (buildId && config) {
|
|
180
211
|
try {
|
|
181
|
-
|
|
212
|
+
let client = createApiClient({
|
|
182
213
|
baseUrl: config.apiUrl,
|
|
183
214
|
token: config.apiKey,
|
|
184
215
|
command: 'upload'
|
|
185
216
|
});
|
|
186
|
-
|
|
187
|
-
await
|
|
217
|
+
let executionTime = Date.now() - uploadStartTime;
|
|
218
|
+
await finalizeBuild(client, buildId, false, executionTime);
|
|
188
219
|
} catch {
|
|
189
220
|
// Silent fail on cleanup
|
|
190
221
|
}
|
|
191
222
|
}
|
|
192
223
|
// Use user-friendly error message if available
|
|
193
|
-
|
|
224
|
+
let errorMessage = error?.getUserMessage ? error.getUserMessage() : error.message;
|
|
194
225
|
output.error(errorMessage || 'Upload failed', error);
|
|
195
|
-
|
|
226
|
+
exit(1);
|
|
227
|
+
return {
|
|
228
|
+
success: false,
|
|
229
|
+
error
|
|
230
|
+
};
|
|
196
231
|
}
|
|
197
232
|
}
|
|
198
233
|
|
package/dist/commands/whoami.js
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* Shows current user and authentication status
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { createAuthClient, createTokenStore, getAuthTokens, whoami } from '../auth/index.js';
|
|
7
7
|
import { getApiUrl } from '../utils/environment-config.js';
|
|
8
|
-
import { getAuthTokens } from '../utils/global-config.js';
|
|
9
8
|
import * as output from '../utils/output.js';
|
|
10
9
|
|
|
11
10
|
/**
|
|
@@ -38,10 +37,11 @@ export async function whoamiCommand(options = {}, globalOptions = {}) {
|
|
|
38
37
|
|
|
39
38
|
// Get current user info
|
|
40
39
|
output.startSpinner('Fetching user information...');
|
|
41
|
-
|
|
40
|
+
let client = createAuthClient({
|
|
42
41
|
baseUrl: options.apiUrl || getApiUrl()
|
|
43
42
|
});
|
|
44
|
-
|
|
43
|
+
let tokenStore = createTokenStore();
|
|
44
|
+
let response = await whoami(client, tokenStore);
|
|
45
45
|
output.stopSpinner();
|
|
46
46
|
|
|
47
47
|
// Output in JSON mode
|