@vizzly-testing/cli 0.20.1-beta.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.
Files changed (36) hide show
  1. package/dist/cli.js +177 -2
  2. package/dist/client/index.js +144 -77
  3. package/dist/commands/doctor.js +118 -33
  4. package/dist/commands/finalize.js +8 -3
  5. package/dist/commands/init.js +13 -18
  6. package/dist/commands/login.js +42 -49
  7. package/dist/commands/logout.js +13 -5
  8. package/dist/commands/project.js +95 -67
  9. package/dist/commands/run.js +32 -6
  10. package/dist/commands/status.js +81 -50
  11. package/dist/commands/tdd-daemon.js +61 -32
  12. package/dist/commands/tdd.js +4 -25
  13. package/dist/commands/upload.js +18 -9
  14. package/dist/commands/whoami.js +40 -38
  15. package/dist/reporter/reporter-bundle.css +1 -1
  16. package/dist/reporter/reporter-bundle.iife.js +11 -11
  17. package/dist/server/handlers/tdd-handler.js +113 -7
  18. package/dist/server/http-server.js +9 -3
  19. package/dist/server/routers/baseline.js +58 -0
  20. package/dist/server/routers/dashboard.js +10 -6
  21. package/dist/server/routers/screenshot.js +32 -0
  22. package/dist/server-manager/core.js +5 -2
  23. package/dist/server-manager/operations.js +2 -1
  24. package/dist/tdd/tdd-service.js +190 -126
  25. package/dist/types/client.d.ts +25 -2
  26. package/dist/utils/colors.js +187 -39
  27. package/dist/utils/config-loader.js +3 -6
  28. package/dist/utils/context.js +228 -0
  29. package/dist/utils/output.js +449 -14
  30. package/docs/api-reference.md +173 -8
  31. package/docs/tui-elements.md +560 -0
  32. package/package.json +12 -7
  33. package/dist/report-generator/core.js +0 -315
  34. package/dist/report-generator/index.js +0 -8
  35. package/dist/report-generator/operations.js +0 -196
  36. package/dist/services/static-report-generator.js +0 -65
@@ -1,315 +0,0 @@
1
- /**
2
- * Report Generator Core - Pure functions for report generation
3
- *
4
- * No I/O, no side effects - just data transformations.
5
- */
6
-
7
- import { join } from 'node:path';
8
-
9
- // ============================================================================
10
- // Constants
11
- // ============================================================================
12
-
13
- export const DEFAULT_REPORT_DIR_NAME = 'report';
14
- export const BUNDLE_FILENAME = 'reporter-bundle.js';
15
- export const CSS_FILENAME = 'reporter-bundle.css';
16
- export const INDEX_FILENAME = 'index.html';
17
-
18
- // ============================================================================
19
- // Path Building
20
- // ============================================================================
21
-
22
- /**
23
- * Build report directory path
24
- * @param {string} workingDir - Working directory
25
- * @returns {string} Report directory path
26
- */
27
- export function buildReportDir(workingDir) {
28
- return join(workingDir, '.vizzly', DEFAULT_REPORT_DIR_NAME);
29
- }
30
-
31
- /**
32
- * Build report HTML path
33
- * @param {string} reportDir - Report directory
34
- * @returns {string} Report HTML path
35
- */
36
- export function buildReportPath(reportDir) {
37
- return join(reportDir, INDEX_FILENAME);
38
- }
39
-
40
- /**
41
- * Build bundle source paths
42
- * @param {string} projectRoot - Project root directory
43
- * @returns {{ bundlePath: string, cssPath: string }}
44
- */
45
- export function buildBundleSourcePaths(projectRoot) {
46
- return {
47
- bundlePath: join(projectRoot, 'dist', 'reporter', 'reporter-bundle.iife.js'),
48
- cssPath: join(projectRoot, 'dist', 'reporter', 'reporter-bundle.css')
49
- };
50
- }
51
-
52
- /**
53
- * Build bundle destination paths
54
- * @param {string} reportDir - Report directory
55
- * @returns {{ bundleDest: string, cssDest: string }}
56
- */
57
- export function buildBundleDestPaths(reportDir) {
58
- return {
59
- bundleDest: join(reportDir, BUNDLE_FILENAME),
60
- cssDest: join(reportDir, CSS_FILENAME)
61
- };
62
- }
63
-
64
- // ============================================================================
65
- // Validation
66
- // ============================================================================
67
-
68
- /**
69
- * Validate report data
70
- * @param {any} reportData - Report data to validate
71
- * @returns {{ valid: boolean, error: string|null }}
72
- */
73
- export function validateReportData(reportData) {
74
- if (!reportData || typeof reportData !== 'object') {
75
- return {
76
- valid: false,
77
- error: 'Invalid report data provided'
78
- };
79
- }
80
- return {
81
- valid: true,
82
- error: null
83
- };
84
- }
85
-
86
- /**
87
- * Check if bundles exist
88
- * @param {boolean} bundleExists - Whether bundle JS exists
89
- * @param {boolean} cssExists - Whether bundle CSS exists
90
- * @returns {{ valid: boolean, error: string|null }}
91
- */
92
- export function validateBundlesExist(bundleExists, cssExists) {
93
- if (!bundleExists || !cssExists) {
94
- return {
95
- valid: false,
96
- error: 'Reporter bundles not found. Run "npm run build:reporter" first.'
97
- };
98
- }
99
- return {
100
- valid: true,
101
- error: null
102
- };
103
- }
104
-
105
- // ============================================================================
106
- // Data Serialization
107
- // ============================================================================
108
-
109
- /**
110
- * Safely serialize data for embedding in HTML script tag
111
- * Escapes characters that could break out of script context
112
- * @param {Object} data - Data to serialize
113
- * @returns {string} Safely serialized JSON string
114
- */
115
- export function serializeForHtml(data) {
116
- return JSON.stringify(data).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
117
- }
118
-
119
- // ============================================================================
120
- // HTML Template Generation
121
- // ============================================================================
122
-
123
- /**
124
- * Generate the main HTML template with React reporter
125
- * @param {Object} options - Template options
126
- * @param {string} options.serializedData - Serialized report data
127
- * @param {string} options.timestamp - ISO timestamp string
128
- * @param {string} options.displayDate - Human-readable date string
129
- * @returns {string} HTML content
130
- */
131
- export function generateMainTemplate({
132
- serializedData,
133
- timestamp,
134
- displayDate
135
- }) {
136
- return `<!DOCTYPE html>
137
- <html lang="en">
138
- <head>
139
- <meta charset="UTF-8">
140
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
141
- <title>Vizzly Dev Report - ${displayDate}</title>
142
- <link rel="stylesheet" href="./reporter-bundle.css">
143
- <style>
144
- /* Loading spinner styles */
145
- .reporter-loading {
146
- display: flex;
147
- align-items: center;
148
- justify-content: center;
149
- min-height: 100vh;
150
- background: #0f172a;
151
- color: #f59e0b;
152
- }
153
- .spinner {
154
- width: 48px;
155
- height: 48px;
156
- border: 4px solid rgba(245, 158, 11, 0.2);
157
- border-top-color: #f59e0b;
158
- border-radius: 50%;
159
- animation: spin 1s linear infinite;
160
- margin-bottom: 1rem;
161
- }
162
- @keyframes spin {
163
- to { transform: rotate(360deg); }
164
- }
165
- </style>
166
- </head>
167
- <body>
168
- <div id="vizzly-reporter-root">
169
- <div class="reporter-loading">
170
- <div style="text-align: center;">
171
- <div class="spinner"></div>
172
- <p>Loading Vizzly Report...</p>
173
- </div>
174
- </div>
175
- </div>
176
-
177
- <script>
178
- // Embedded report data (static mode)
179
- window.VIZZLY_REPORTER_DATA = ${serializedData};
180
- window.VIZZLY_STATIC_MODE = true;
181
-
182
- // Generate timestamp for "generated at" display
183
- window.VIZZLY_REPORT_GENERATED_AT = "${timestamp}";
184
-
185
- console.log('Vizzly Static Report loaded');
186
- console.log('Report data:', window.VIZZLY_REPORTER_DATA?.summary);
187
- </script>
188
- <script src="./reporter-bundle.js"></script>
189
- </body>
190
- </html>`;
191
- }
192
-
193
- /**
194
- * Generate fallback HTML template (when bundles are missing)
195
- * @param {Object} options - Template options
196
- * @param {Object} options.summary - Report summary
197
- * @param {Array} options.comparisons - Comparison results
198
- * @param {string} options.displayDate - Human-readable date string
199
- * @returns {string} HTML content
200
- */
201
- export function generateFallbackTemplate({
202
- summary,
203
- comparisons,
204
- displayDate
205
- }) {
206
- let failed = comparisons.filter(c => c.status === 'failed');
207
- let failedSection = failed.length > 0 ? `
208
- <h2>Failed Comparisons</h2>
209
- <ul>
210
- ${failed.map(c => `<li>${c.name} - ${c.diffPercentage || 0}% difference</li>`).join('')}
211
- </ul>
212
- ` : '<p style="text-align: center; font-size: 1.5rem;">✅ All tests passed!</p>';
213
- return `<!DOCTYPE html>
214
- <html lang="en">
215
- <head>
216
- <meta charset="UTF-8">
217
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
218
- <title>Vizzly Dev Report</title>
219
- <style>
220
- body {
221
- font-family: system-ui, -apple-system, sans-serif;
222
- background: #0f172a;
223
- color: #e2e8f0;
224
- padding: 2rem;
225
- }
226
- .container { max-width: 1200px; margin: 0 auto; }
227
- .header { text-align: center; margin-bottom: 2rem; }
228
- .summary {
229
- display: flex;
230
- gap: 2rem;
231
- justify-content: center;
232
- margin: 2rem 0;
233
- }
234
- .stat { text-align: center; }
235
- .stat-number {
236
- font-size: 3rem;
237
- font-weight: bold;
238
- display: block;
239
- }
240
- .warning {
241
- background: #fef3c7;
242
- color: #92400e;
243
- padding: 1rem;
244
- border-radius: 0.5rem;
245
- margin: 2rem 0;
246
- }
247
- </style>
248
- </head>
249
- <body>
250
- <div class="container">
251
- <div class="header">
252
- <h1>🐻 Vizzly Dev Report</h1>
253
- <p>Generated: ${displayDate}</p>
254
- </div>
255
-
256
- <div class="summary">
257
- <div class="stat">
258
- <span class="stat-number">${summary.total || 0}</span>
259
- <span>Total</span>
260
- </div>
261
- <div class="stat">
262
- <span class="stat-number" style="color: #10b981;">${summary.passed || 0}</span>
263
- <span>Passed</span>
264
- </div>
265
- <div class="stat">
266
- <span class="stat-number" style="color: #ef4444;">${summary.failed || 0}</span>
267
- <span>Failed</span>
268
- </div>
269
- </div>
270
-
271
- <div class="warning">
272
- <strong>⚠️ Limited Report</strong>
273
- <p>This is a fallback report. For the full interactive experience, ensure the reporter bundle is built:</p>
274
- <code>npm run build:reporter</code>
275
- </div>
276
-
277
- ${failedSection}
278
- </div>
279
- </body>
280
- </html>`;
281
- }
282
-
283
- /**
284
- * Build HTML content for the report
285
- * @param {Object} reportData - Report data
286
- * @param {Date} date - Current date
287
- * @returns {string} HTML content
288
- */
289
- export function buildHtmlContent(reportData, date) {
290
- let serializedData = serializeForHtml(reportData);
291
- let timestamp = date.toISOString();
292
- let displayDate = date.toLocaleString();
293
- return generateMainTemplate({
294
- serializedData,
295
- timestamp,
296
- displayDate
297
- });
298
- }
299
-
300
- /**
301
- * Build fallback HTML content
302
- * @param {Object} reportData - Report data
303
- * @param {Date} date - Current date
304
- * @returns {string} HTML content
305
- */
306
- export function buildFallbackHtmlContent(reportData, date) {
307
- let summary = reportData.summary || {};
308
- let comparisons = reportData.comparisons || [];
309
- let displayDate = date.toLocaleString();
310
- return generateFallbackTemplate({
311
- summary,
312
- comparisons,
313
- displayDate
314
- });
315
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Report Generator module
3
- *
4
- * Pure functions in core.js, I/O operations in operations.js.
5
- */
6
-
7
- export * from './core.js';
8
- export * from './operations.js';
@@ -1,196 +0,0 @@
1
- /**
2
- * Report Generator Operations - I/O operations with dependency injection
3
- *
4
- * Each operation takes its dependencies as parameters for testability.
5
- */
6
-
7
- import { buildBundleDestPaths, buildBundleSourcePaths, buildHtmlContent, buildReportDir, buildReportPath, validateBundlesExist, validateReportData } from './core.js';
8
-
9
- // ============================================================================
10
- // File Operations
11
- // ============================================================================
12
-
13
- /**
14
- * Ensure a directory exists
15
- * @param {Object} options - Options
16
- * @param {string} options.path - Directory path
17
- * @param {Object} options.deps - Dependencies
18
- * @param {Function} options.deps.mkdir - mkdir function
19
- * @returns {Promise<void>}
20
- */
21
- export async function ensureDirectory({
22
- path,
23
- deps
24
- }) {
25
- let {
26
- mkdir
27
- } = deps;
28
- await mkdir(path, {
29
- recursive: true
30
- });
31
- }
32
-
33
- /**
34
- * Check if bundles exist
35
- * @param {Object} options - Options
36
- * @param {string} options.projectRoot - Project root directory
37
- * @param {Object} options.deps - Dependencies
38
- * @param {Function} options.deps.existsSync - existsSync function
39
- * @returns {{ bundleExists: boolean, cssExists: boolean, bundlePath: string, cssPath: string }}
40
- */
41
- export function checkBundlesExist({
42
- projectRoot,
43
- deps
44
- }) {
45
- let {
46
- existsSync
47
- } = deps;
48
- let {
49
- bundlePath,
50
- cssPath
51
- } = buildBundleSourcePaths(projectRoot);
52
- return {
53
- bundleExists: existsSync(bundlePath),
54
- cssExists: existsSync(cssPath),
55
- bundlePath,
56
- cssPath
57
- };
58
- }
59
-
60
- /**
61
- * Copy bundle files to report directory
62
- * @param {Object} options - Options
63
- * @param {string} options.bundlePath - Source bundle path
64
- * @param {string} options.cssPath - Source CSS path
65
- * @param {string} options.reportDir - Report directory
66
- * @param {Object} options.deps - Dependencies
67
- * @param {Function} options.deps.copyFile - copyFile function
68
- * @returns {Promise<void>}
69
- */
70
- export async function copyBundles({
71
- bundlePath,
72
- cssPath,
73
- reportDir,
74
- deps
75
- }) {
76
- let {
77
- copyFile
78
- } = deps;
79
- let {
80
- bundleDest,
81
- cssDest
82
- } = buildBundleDestPaths(reportDir);
83
- await copyFile(bundlePath, bundleDest);
84
- await copyFile(cssPath, cssDest);
85
- }
86
-
87
- /**
88
- * Write HTML content to file
89
- * @param {Object} options - Options
90
- * @param {string} options.path - File path
91
- * @param {string} options.content - HTML content
92
- * @param {Object} options.deps - Dependencies
93
- * @param {Function} options.deps.writeFile - writeFile function
94
- * @returns {Promise<void>}
95
- */
96
- export async function writeHtmlFile({
97
- path,
98
- content,
99
- deps
100
- }) {
101
- let {
102
- writeFile
103
- } = deps;
104
- await writeFile(path, content, 'utf8');
105
- }
106
-
107
- // ============================================================================
108
- // Main Report Generation
109
- // ============================================================================
110
-
111
- /**
112
- * Generate a static HTML report with React reporter
113
- * @param {Object} options - Options
114
- * @param {Object} options.reportData - Report data
115
- * @param {string} options.workingDir - Working directory
116
- * @param {string} options.projectRoot - Project root directory
117
- * @param {Object} options.deps - Dependencies
118
- * @param {Function} options.deps.mkdir - mkdir function
119
- * @param {Function} options.deps.existsSync - existsSync function
120
- * @param {Function} options.deps.copyFile - copyFile function
121
- * @param {Function} options.deps.writeFile - writeFile function
122
- * @param {Object} options.deps.output - Output utilities
123
- * @param {Function} options.deps.getDate - Function that returns current Date
124
- * @returns {Promise<string>} Path to generated report
125
- */
126
- export async function generateReport({
127
- reportData,
128
- workingDir,
129
- projectRoot,
130
- deps
131
- }) {
132
- let {
133
- mkdir,
134
- existsSync,
135
- copyFile,
136
- writeFile,
137
- output,
138
- getDate
139
- } = deps;
140
-
141
- // Validate report data
142
- let validation = validateReportData(reportData);
143
- if (!validation.valid) {
144
- throw new Error(validation.error);
145
- }
146
- let reportDir = buildReportDir(workingDir);
147
- let reportPath = buildReportPath(reportDir);
148
- try {
149
- // Ensure report directory exists
150
- await ensureDirectory({
151
- path: reportDir,
152
- deps: {
153
- mkdir
154
- }
155
- });
156
-
157
- // Check if bundles exist
158
- let bundleCheck = checkBundlesExist({
159
- projectRoot,
160
- deps: {
161
- existsSync
162
- }
163
- });
164
- let bundleValidation = validateBundlesExist(bundleCheck.bundleExists, bundleCheck.cssExists);
165
- if (!bundleValidation.valid) {
166
- throw new Error(bundleValidation.error);
167
- }
168
-
169
- // Copy bundles to report directory
170
- await copyBundles({
171
- bundlePath: bundleCheck.bundlePath,
172
- cssPath: bundleCheck.cssPath,
173
- reportDir,
174
- deps: {
175
- copyFile
176
- }
177
- });
178
-
179
- // Generate HTML content
180
- let htmlContent = buildHtmlContent(reportData, getDate());
181
-
182
- // Write HTML file
183
- await writeHtmlFile({
184
- path: reportPath,
185
- content: htmlContent,
186
- deps: {
187
- writeFile
188
- }
189
- });
190
- output.debug('report', 'generated static report');
191
- return reportPath;
192
- } catch (error) {
193
- output.error(`Failed to generate static report: ${error.message}`);
194
- throw new Error(`Report generation failed: ${error.message}`);
195
- }
196
- }
@@ -1,65 +0,0 @@
1
- /**
2
- * Static Report Generator using React Reporter
3
- * Generates a self-contained HTML file with the React dashboard and embedded data
4
- *
5
- * This class is a thin wrapper around the functional report-generator module.
6
- * For new code, consider using the functions directly from '../report-generator/'.
7
- */
8
-
9
- import { existsSync } from 'node:fs';
10
- import { copyFile, mkdir, writeFile } from 'node:fs/promises';
11
- import { dirname, join } from 'node:path';
12
- import { fileURLToPath } from 'node:url';
13
- import { buildFallbackHtmlContent, buildHtmlContent, buildReportDir, buildReportPath, generateReport } from '../report-generator/index.js';
14
- import * as output from '../utils/output.js';
15
- const __filename = fileURLToPath(import.meta.url);
16
- const __dirname = dirname(__filename);
17
- const PROJECT_ROOT = join(__dirname, '..', '..');
18
- export class StaticReportGenerator {
19
- constructor(workingDir, config) {
20
- this.workingDir = workingDir;
21
- this.config = config;
22
- this.reportDir = buildReportDir(workingDir);
23
- this.reportPath = buildReportPath(this.reportDir);
24
- }
25
-
26
- /**
27
- * Generate static HTML report with React reporter bundle
28
- * @param {Object} reportData - Complete report data (same format as live dashboard)
29
- * @returns {Promise<string>} Path to generated report
30
- */
31
- async generateReport(reportData) {
32
- return generateReport({
33
- reportData,
34
- workingDir: this.workingDir,
35
- projectRoot: PROJECT_ROOT,
36
- deps: {
37
- mkdir,
38
- existsSync,
39
- copyFile,
40
- writeFile,
41
- output,
42
- getDate: () => new Date()
43
- }
44
- });
45
- }
46
-
47
- /**
48
- * Generate HTML template with embedded React app
49
- * @param {Object} reportData - Report data to embed
50
- * @returns {string} HTML content
51
- * @deprecated Use buildHtmlContent from report-generator/core.js
52
- */
53
- generateHtmlTemplate(reportData) {
54
- return buildHtmlContent(reportData, new Date());
55
- }
56
-
57
- /**
58
- * Generate a minimal HTML report when bundles are missing (fallback)
59
- * @param {Object} reportData - Report data
60
- * @returns {string} Minimal HTML content
61
- */
62
- generateFallbackHtml(reportData) {
63
- return buildFallbackHtmlContent(reportData, new Date());
64
- }
65
- }