@vizzly-testing/cli 0.20.0 → 0.20.1-beta.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/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 +178 -3
- package/dist/client/index.js +144 -77
- package/dist/commands/doctor.js +121 -36
- package/dist/commands/finalize.js +49 -18
- package/dist/commands/init.js +13 -18
- package/dist/commands/login.js +49 -55
- package/dist/commands/logout.js +17 -9
- package/dist/commands/project.js +100 -71
- package/dist/commands/run.js +189 -95
- package/dist/commands/status.js +101 -66
- package/dist/commands/tdd-daemon.js +61 -32
- package/dist/commands/tdd.js +104 -98
- package/dist/commands/upload.js +78 -34
- package/dist/commands/whoami.js +44 -42
- 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/reporter/reporter-bundle.css +1 -1
- 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 +191 -53
- package/dist/server/http-server.js +9 -3
- package/dist/server/routers/baseline.js +58 -0
- package/dist/server/routers/dashboard.js +10 -6
- package/dist/server/routers/screenshot.js +32 -0
- package/dist/server-manager/core.js +186 -0
- package/dist/server-manager/index.js +81 -0
- package/dist/server-manager/operations.js +209 -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/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 +1145 -0
- package/dist/test-runner/core.js +255 -0
- package/dist/test-runner/index.js +13 -0
- package/dist/test-runner/operations.js +483 -0
- package/dist/types/client.d.ts +25 -2
- package/dist/uploader/core.js +396 -0
- package/dist/uploader/index.js +11 -0
- package/dist/uploader/operations.js +412 -0
- package/dist/utils/colors.js +187 -39
- package/dist/utils/config-loader.js +3 -6
- package/dist/utils/context.js +228 -0
- package/dist/utils/output.js +449 -14
- package/docs/api-reference.md +173 -8
- package/docs/tui-elements.md +560 -0
- package/package.json +13 -13
- 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/static-report-generator.js +0 -207
- package/dist/services/tdd-service.js +0 -1437
package/dist/commands/project.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
7
|
import readline from 'node:readline';
|
|
8
|
-
import {
|
|
8
|
+
import { createAuthClient, createTokenStore, getAuthTokens, whoami } from '../auth/index.js';
|
|
9
9
|
import { getApiUrl } from '../utils/environment-config.js';
|
|
10
|
-
import { deleteProjectMapping,
|
|
10
|
+
import { deleteProjectMapping, getProjectMapping, getProjectMappings, saveProjectMapping } from '../utils/global-config.js';
|
|
11
11
|
import * as output from '../utils/output.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -22,43 +22,42 @@ export async function projectSelectCommand(options = {}, globalOptions = {}) {
|
|
|
22
22
|
color: !globalOptions.noColor
|
|
23
23
|
});
|
|
24
24
|
try {
|
|
25
|
+
output.header('project:select');
|
|
26
|
+
|
|
25
27
|
// Check authentication
|
|
26
|
-
|
|
28
|
+
let auth = await getAuthTokens();
|
|
27
29
|
if (!auth || !auth.accessToken) {
|
|
28
30
|
output.error('Not authenticated');
|
|
29
|
-
output.
|
|
30
|
-
output.info('Run "vizzly login" to authenticate first');
|
|
31
|
+
output.hint('Run "vizzly login" to authenticate first');
|
|
31
32
|
process.exit(1);
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
+
let client = createAuthClient({
|
|
34
35
|
baseUrl: options.apiUrl || getApiUrl()
|
|
35
36
|
});
|
|
37
|
+
let tokenStore = createTokenStore();
|
|
36
38
|
|
|
37
39
|
// Get user info to show organizations
|
|
38
40
|
output.startSpinner('Fetching organizations...');
|
|
39
|
-
|
|
41
|
+
let userInfo = await whoami(client, tokenStore);
|
|
40
42
|
output.stopSpinner();
|
|
41
43
|
if (!userInfo.organizations || userInfo.organizations.length === 0) {
|
|
42
44
|
output.error('No organizations found');
|
|
43
|
-
output.
|
|
44
|
-
output.info('Create an organization at https://vizzly.dev');
|
|
45
|
+
output.hint('Create an organization at https://vizzly.dev');
|
|
45
46
|
process.exit(1);
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
// Select organization
|
|
49
|
-
output.
|
|
50
|
-
output.info('Select an organization:');
|
|
51
|
-
output.blank();
|
|
50
|
+
output.labelValue('Organizations', '');
|
|
52
51
|
userInfo.organizations.forEach((org, index) => {
|
|
53
52
|
output.print(` ${index + 1}. ${org.name} (@${org.slug})`);
|
|
54
53
|
});
|
|
55
54
|
output.blank();
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
let orgChoice = await promptNumber('Enter number', 1, userInfo.organizations.length);
|
|
56
|
+
let selectedOrg = userInfo.organizations[orgChoice - 1];
|
|
58
57
|
|
|
59
58
|
// List projects for organization
|
|
60
59
|
output.startSpinner(`Fetching projects for ${selectedOrg.name}...`);
|
|
61
|
-
|
|
60
|
+
let response = await makeAuthenticatedRequest(`${options.apiUrl || getApiUrl()}/api/project`, {
|
|
62
61
|
headers: {
|
|
63
62
|
Authorization: `Bearer ${auth.accessToken}`,
|
|
64
63
|
'X-Organization': selectedOrg.slug
|
|
@@ -67,28 +66,26 @@ export async function projectSelectCommand(options = {}, globalOptions = {}) {
|
|
|
67
66
|
output.stopSpinner();
|
|
68
67
|
|
|
69
68
|
// Handle both array response and object with projects property
|
|
70
|
-
|
|
69
|
+
let projects = Array.isArray(response) ? response : response.projects || [];
|
|
71
70
|
if (projects.length === 0) {
|
|
72
71
|
output.error('No projects found');
|
|
73
|
-
output.
|
|
74
|
-
output.info(`Create a project in ${selectedOrg.name} at https://vizzly.dev`);
|
|
72
|
+
output.hint(`Create a project in ${selectedOrg.name} at https://vizzly.dev`);
|
|
75
73
|
process.exit(1);
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
// Select project
|
|
79
77
|
output.blank();
|
|
80
|
-
output.
|
|
81
|
-
output.blank();
|
|
78
|
+
output.labelValue('Projects', '');
|
|
82
79
|
projects.forEach((project, index) => {
|
|
83
80
|
output.print(` ${index + 1}. ${project.name} (${project.slug})`);
|
|
84
81
|
});
|
|
85
82
|
output.blank();
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
let projectChoice = await promptNumber('Enter number', 1, projects.length);
|
|
84
|
+
let selectedProject = projects[projectChoice - 1];
|
|
88
85
|
|
|
89
86
|
// Create API token for project
|
|
90
87
|
output.startSpinner(`Creating API token for ${selectedProject.name}...`);
|
|
91
|
-
|
|
88
|
+
let tokenResponse = await makeAuthenticatedRequest(`${options.apiUrl || getApiUrl()}/api/project/${selectedProject.slug}/tokens`, {
|
|
92
89
|
method: 'POST',
|
|
93
90
|
headers: {
|
|
94
91
|
Authorization: `Bearer ${auth.accessToken}`,
|
|
@@ -103,18 +100,20 @@ export async function projectSelectCommand(options = {}, globalOptions = {}) {
|
|
|
103
100
|
output.stopSpinner();
|
|
104
101
|
|
|
105
102
|
// Save project mapping
|
|
106
|
-
|
|
103
|
+
let currentDir = resolve(process.cwd());
|
|
107
104
|
await saveProjectMapping(currentDir, {
|
|
108
105
|
token: tokenResponse.token,
|
|
109
106
|
projectSlug: selectedProject.slug,
|
|
110
107
|
projectName: selectedProject.name,
|
|
111
108
|
organizationSlug: selectedOrg.slug
|
|
112
109
|
});
|
|
113
|
-
output.
|
|
110
|
+
output.complete('Project configured');
|
|
114
111
|
output.blank();
|
|
115
|
-
output.
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
output.keyValue({
|
|
113
|
+
Project: selectedProject.name,
|
|
114
|
+
Organization: selectedOrg.name,
|
|
115
|
+
Directory: currentDir
|
|
116
|
+
});
|
|
118
117
|
output.cleanup();
|
|
119
118
|
} catch (error) {
|
|
120
119
|
output.stopSpinner();
|
|
@@ -135,12 +134,17 @@ export async function projectListCommand(_options = {}, globalOptions = {}) {
|
|
|
135
134
|
color: !globalOptions.noColor
|
|
136
135
|
});
|
|
137
136
|
try {
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
let mappings = await getProjectMappings();
|
|
138
|
+
let paths = Object.keys(mappings);
|
|
140
139
|
if (paths.length === 0) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
140
|
+
if (globalOptions.json) {
|
|
141
|
+
output.data({});
|
|
142
|
+
} else {
|
|
143
|
+
output.header('project:list');
|
|
144
|
+
output.print(' No projects configured');
|
|
145
|
+
output.blank();
|
|
146
|
+
output.hint('Run "vizzly project:select" to configure a project');
|
|
147
|
+
}
|
|
144
148
|
output.cleanup();
|
|
145
149
|
return;
|
|
146
150
|
}
|
|
@@ -149,22 +153,29 @@ export async function projectListCommand(_options = {}, globalOptions = {}) {
|
|
|
149
153
|
output.cleanup();
|
|
150
154
|
return;
|
|
151
155
|
}
|
|
152
|
-
output.
|
|
153
|
-
output.
|
|
154
|
-
|
|
155
|
-
for (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
156
|
+
output.header('project:list');
|
|
157
|
+
let colors = output.getColors();
|
|
158
|
+
let currentDir = resolve(process.cwd());
|
|
159
|
+
for (let path of paths) {
|
|
160
|
+
let mapping = mappings[path];
|
|
161
|
+
let isCurrent = path === currentDir;
|
|
162
|
+
let marker = isCurrent ? colors.brand.amber('→') : ' ';
|
|
163
|
+
output.print(`${marker} ${isCurrent ? colors.bold(path) : path}`);
|
|
164
|
+
output.keyValue({
|
|
165
|
+
Project: `${mapping.projectName} (${mapping.projectSlug})`,
|
|
166
|
+
Org: mapping.organizationSlug
|
|
167
|
+
}, {
|
|
168
|
+
indent: 4
|
|
169
|
+
});
|
|
165
170
|
if (globalOptions.verbose) {
|
|
166
|
-
|
|
167
|
-
|
|
171
|
+
// Extract token string (handle both string and object formats)
|
|
172
|
+
let tokenStr = typeof mapping.token === 'string' ? mapping.token : mapping.token?.token || '[invalid token]';
|
|
173
|
+
output.hint(`Token: ${tokenStr.substring(0, 20)}...`, {
|
|
174
|
+
indent: 4
|
|
175
|
+
});
|
|
176
|
+
output.hint(`Created: ${new Date(mapping.createdAt).toLocaleString()}`, {
|
|
177
|
+
indent: 4
|
|
178
|
+
});
|
|
168
179
|
}
|
|
169
180
|
output.blank();
|
|
170
181
|
}
|
|
@@ -187,17 +198,16 @@ export async function projectTokenCommand(_options = {}, globalOptions = {}) {
|
|
|
187
198
|
color: !globalOptions.noColor
|
|
188
199
|
});
|
|
189
200
|
try {
|
|
190
|
-
|
|
191
|
-
|
|
201
|
+
let currentDir = resolve(process.cwd());
|
|
202
|
+
let mapping = await getProjectMapping(currentDir);
|
|
192
203
|
if (!mapping) {
|
|
193
204
|
output.error('No project configured for this directory');
|
|
194
|
-
output.
|
|
195
|
-
output.info('Run "vizzly project:select" to configure a project');
|
|
205
|
+
output.hint('Run "vizzly project:select" to configure a project');
|
|
196
206
|
process.exit(1);
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
// Extract token string (handle both string and object formats)
|
|
200
|
-
|
|
210
|
+
let tokenStr = typeof mapping.token === 'string' ? mapping.token : mapping.token?.token || '[invalid token]';
|
|
201
211
|
if (globalOptions.json) {
|
|
202
212
|
output.data({
|
|
203
213
|
token: tokenStr,
|
|
@@ -207,12 +217,15 @@ export async function projectTokenCommand(_options = {}, globalOptions = {}) {
|
|
|
207
217
|
output.cleanup();
|
|
208
218
|
return;
|
|
209
219
|
}
|
|
210
|
-
output.
|
|
211
|
-
output.
|
|
212
|
-
|
|
220
|
+
output.header('project:token');
|
|
221
|
+
output.printBox(tokenStr, {
|
|
222
|
+
title: 'Token'
|
|
223
|
+
});
|
|
213
224
|
output.blank();
|
|
214
|
-
output.
|
|
215
|
-
|
|
225
|
+
output.keyValue({
|
|
226
|
+
Project: `${mapping.projectName} (${mapping.projectSlug})`,
|
|
227
|
+
Org: mapping.organizationSlug
|
|
228
|
+
});
|
|
216
229
|
output.cleanup();
|
|
217
230
|
} catch (error) {
|
|
218
231
|
output.error('Failed to get project token', error);
|
|
@@ -275,31 +288,47 @@ export async function projectRemoveCommand(_options = {}, globalOptions = {}) {
|
|
|
275
288
|
color: !globalOptions.noColor
|
|
276
289
|
});
|
|
277
290
|
try {
|
|
278
|
-
|
|
279
|
-
|
|
291
|
+
let currentDir = resolve(process.cwd());
|
|
292
|
+
let mapping = await getProjectMapping(currentDir);
|
|
280
293
|
if (!mapping) {
|
|
281
|
-
|
|
294
|
+
if (globalOptions.json) {
|
|
295
|
+
output.data({
|
|
296
|
+
removed: false,
|
|
297
|
+
reason: 'not_configured'
|
|
298
|
+
});
|
|
299
|
+
} else {
|
|
300
|
+
output.header('project:remove');
|
|
301
|
+
output.print(' No project configured for this directory');
|
|
302
|
+
}
|
|
282
303
|
output.cleanup();
|
|
283
304
|
return;
|
|
284
305
|
}
|
|
285
306
|
|
|
286
307
|
// Confirm removal
|
|
308
|
+
output.header('project:remove');
|
|
309
|
+
output.labelValue('Current configuration', '');
|
|
310
|
+
output.keyValue({
|
|
311
|
+
Project: `${mapping.projectName} (${mapping.projectSlug})`,
|
|
312
|
+
Org: mapping.organizationSlug,
|
|
313
|
+
Directory: currentDir
|
|
314
|
+
});
|
|
287
315
|
output.blank();
|
|
288
|
-
|
|
289
|
-
output.print(` Project: ${mapping.projectName} (${mapping.projectSlug})`);
|
|
290
|
-
output.print(` Organization: ${mapping.organizationSlug}`);
|
|
291
|
-
output.print(` Directory: ${currentDir}`);
|
|
292
|
-
output.blank();
|
|
293
|
-
const confirmed = await promptConfirm('Remove this project configuration?');
|
|
316
|
+
let confirmed = await promptConfirm('Remove this project configuration?');
|
|
294
317
|
if (!confirmed) {
|
|
295
|
-
output.
|
|
318
|
+
output.print(' Cancelled');
|
|
296
319
|
output.cleanup();
|
|
297
320
|
return;
|
|
298
321
|
}
|
|
299
322
|
await deleteProjectMapping(currentDir);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
323
|
+
if (globalOptions.json) {
|
|
324
|
+
output.data({
|
|
325
|
+
removed: true
|
|
326
|
+
});
|
|
327
|
+
} else {
|
|
328
|
+
output.complete('Project configuration removed');
|
|
329
|
+
output.blank();
|
|
330
|
+
output.hint('Run "vizzly project:select" to configure a different project');
|
|
331
|
+
}
|
|
303
332
|
output.cleanup();
|
|
304
333
|
} catch (error) {
|
|
305
334
|
output.error('Failed to remove project configuration', error);
|