@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.
Files changed (84) hide show
  1. package/dist/api/client.js +134 -0
  2. package/dist/api/core.js +341 -0
  3. package/dist/api/endpoints.js +314 -0
  4. package/dist/api/index.js +19 -0
  5. package/dist/auth/client.js +91 -0
  6. package/dist/auth/core.js +176 -0
  7. package/dist/auth/index.js +30 -0
  8. package/dist/auth/operations.js +148 -0
  9. package/dist/cli.js +178 -3
  10. package/dist/client/index.js +144 -77
  11. package/dist/commands/doctor.js +121 -36
  12. package/dist/commands/finalize.js +49 -18
  13. package/dist/commands/init.js +13 -18
  14. package/dist/commands/login.js +49 -55
  15. package/dist/commands/logout.js +17 -9
  16. package/dist/commands/project.js +100 -71
  17. package/dist/commands/run.js +189 -95
  18. package/dist/commands/status.js +101 -66
  19. package/dist/commands/tdd-daemon.js +61 -32
  20. package/dist/commands/tdd.js +104 -98
  21. package/dist/commands/upload.js +78 -34
  22. package/dist/commands/whoami.js +44 -42
  23. package/dist/config/core.js +438 -0
  24. package/dist/config/index.js +13 -0
  25. package/dist/config/operations.js +327 -0
  26. package/dist/index.js +1 -1
  27. package/dist/project/core.js +295 -0
  28. package/dist/project/index.js +13 -0
  29. package/dist/project/operations.js +393 -0
  30. package/dist/reporter/reporter-bundle.css +1 -1
  31. package/dist/reporter/reporter-bundle.iife.js +16 -16
  32. package/dist/screenshot-server/core.js +157 -0
  33. package/dist/screenshot-server/index.js +11 -0
  34. package/dist/screenshot-server/operations.js +183 -0
  35. package/dist/sdk/index.js +3 -2
  36. package/dist/server/handlers/api-handler.js +14 -5
  37. package/dist/server/handlers/tdd-handler.js +191 -53
  38. package/dist/server/http-server.js +9 -3
  39. package/dist/server/routers/baseline.js +58 -0
  40. package/dist/server/routers/dashboard.js +10 -6
  41. package/dist/server/routers/screenshot.js +32 -0
  42. package/dist/server-manager/core.js +186 -0
  43. package/dist/server-manager/index.js +81 -0
  44. package/dist/server-manager/operations.js +209 -0
  45. package/dist/services/build-manager.js +2 -69
  46. package/dist/services/index.js +21 -48
  47. package/dist/services/screenshot-server.js +40 -74
  48. package/dist/services/server-manager.js +45 -80
  49. package/dist/services/test-runner.js +90 -250
  50. package/dist/services/uploader.js +56 -358
  51. package/dist/tdd/core/hotspot-coverage.js +112 -0
  52. package/dist/tdd/core/signature.js +101 -0
  53. package/dist/tdd/index.js +19 -0
  54. package/dist/tdd/metadata/baseline-metadata.js +103 -0
  55. package/dist/tdd/metadata/hotspot-metadata.js +93 -0
  56. package/dist/tdd/services/baseline-downloader.js +151 -0
  57. package/dist/tdd/services/baseline-manager.js +166 -0
  58. package/dist/tdd/services/comparison-service.js +230 -0
  59. package/dist/tdd/services/hotspot-service.js +71 -0
  60. package/dist/tdd/services/result-service.js +123 -0
  61. package/dist/tdd/tdd-service.js +1145 -0
  62. package/dist/test-runner/core.js +255 -0
  63. package/dist/test-runner/index.js +13 -0
  64. package/dist/test-runner/operations.js +483 -0
  65. package/dist/types/client.d.ts +25 -2
  66. package/dist/uploader/core.js +396 -0
  67. package/dist/uploader/index.js +11 -0
  68. package/dist/uploader/operations.js +412 -0
  69. package/dist/utils/colors.js +187 -39
  70. package/dist/utils/config-loader.js +3 -6
  71. package/dist/utils/context.js +228 -0
  72. package/dist/utils/output.js +449 -14
  73. package/docs/api-reference.md +173 -8
  74. package/docs/tui-elements.md +560 -0
  75. package/package.json +13 -13
  76. package/dist/services/api-service.js +0 -412
  77. package/dist/services/auth-service.js +0 -226
  78. package/dist/services/config-service.js +0 -369
  79. package/dist/services/html-report-generator.js +0 -455
  80. package/dist/services/project-service.js +0 -326
  81. package/dist/services/report-generator/report.css +0 -411
  82. package/dist/services/report-generator/viewer.js +0 -102
  83. package/dist/services/static-report-generator.js +0 -207
  84. package/dist/services/tdd-service.js +0 -1437
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Screenshot Server Core - Pure functions for screenshot server logic
3
+ *
4
+ * No I/O, no side effects - just data transformations.
5
+ */
6
+
7
+ // ============================================================================
8
+ // Request Validation
9
+ // ============================================================================
10
+
11
+ /**
12
+ * Validate screenshot request body
13
+ * @param {Object} body - Request body
14
+ * @returns {{ valid: boolean, error: string|null }}
15
+ */
16
+ export function validateScreenshotRequest(body) {
17
+ if (!body?.name || !body?.image) {
18
+ return {
19
+ valid: false,
20
+ error: 'name and image are required'
21
+ };
22
+ }
23
+ return {
24
+ valid: true,
25
+ error: null
26
+ };
27
+ }
28
+
29
+ /**
30
+ * Check if request is for the screenshot endpoint
31
+ * @param {string} method - HTTP method
32
+ * @param {string} url - Request URL
33
+ * @returns {boolean}
34
+ */
35
+ export function isScreenshotEndpoint(method, url) {
36
+ return method === 'POST' && url === '/screenshot';
37
+ }
38
+
39
+ // ============================================================================
40
+ // Build ID Handling
41
+ // ============================================================================
42
+
43
+ /**
44
+ * Get effective build ID (falls back to 'default')
45
+ * @param {string|null|undefined} buildId - Build ID from request
46
+ * @returns {string}
47
+ */
48
+ export function getEffectiveBuildId(buildId) {
49
+ return buildId || 'default';
50
+ }
51
+
52
+ // ============================================================================
53
+ // Response Building
54
+ // ============================================================================
55
+
56
+ /**
57
+ * Build success response object
58
+ * @returns {{ status: number, body: Object }}
59
+ */
60
+ export function buildSuccessResponse() {
61
+ return {
62
+ status: 200,
63
+ body: {
64
+ success: true
65
+ }
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Build error response object
71
+ * @param {number} status - HTTP status code
72
+ * @param {string} message - Error message
73
+ * @returns {{ status: number, body: Object }}
74
+ */
75
+ export function buildErrorResponse(status, message) {
76
+ return {
77
+ status,
78
+ body: {
79
+ error: message
80
+ }
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Build not found response
86
+ * @returns {{ status: number, body: Object }}
87
+ */
88
+ export function buildNotFoundResponse() {
89
+ return buildErrorResponse(404, 'Not found');
90
+ }
91
+
92
+ /**
93
+ * Build bad request response
94
+ * @param {string} message - Error message
95
+ * @returns {{ status: number, body: Object }}
96
+ */
97
+ export function buildBadRequestResponse(message) {
98
+ return buildErrorResponse(400, message);
99
+ }
100
+
101
+ /**
102
+ * Build internal error response
103
+ * @returns {{ status: number, body: Object }}
104
+ */
105
+ export function buildInternalErrorResponse() {
106
+ return buildErrorResponse(500, 'Internal server error');
107
+ }
108
+
109
+ // ============================================================================
110
+ // Server Configuration
111
+ // ============================================================================
112
+
113
+ /**
114
+ * Build server listen options
115
+ * @param {Object} config - Server configuration
116
+ * @returns {{ port: number, host: string }}
117
+ */
118
+ export function buildServerListenOptions(config) {
119
+ return {
120
+ port: config?.server?.port || 3000,
121
+ host: '127.0.0.1'
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Build server started message
127
+ * @param {number} port - Server port
128
+ * @returns {string}
129
+ */
130
+ export function buildServerStartedMessage(port) {
131
+ return `Screenshot server listening on http://127.0.0.1:${port}`;
132
+ }
133
+
134
+ /**
135
+ * Build server stopped message
136
+ * @returns {string}
137
+ */
138
+ export function buildServerStoppedMessage() {
139
+ return 'Screenshot server stopped';
140
+ }
141
+
142
+ // ============================================================================
143
+ // Screenshot Data Extraction
144
+ // ============================================================================
145
+
146
+ /**
147
+ * Extract screenshot data from request body
148
+ * @param {Object} body - Request body
149
+ * @returns {{ name: string, image: string, properties?: Object }}
150
+ */
151
+ export function extractScreenshotData(body) {
152
+ return {
153
+ name: body.name,
154
+ image: body.image,
155
+ properties: body.properties
156
+ };
157
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Screenshot Server Module
3
+ *
4
+ * Exports pure functions (core) and I/O operations for screenshot server functionality.
5
+ */
6
+
7
+ // Core - pure functions
8
+ export { buildBadRequestResponse, buildErrorResponse, buildInternalErrorResponse, buildNotFoundResponse, buildServerListenOptions, buildServerStartedMessage, buildServerStoppedMessage, buildSuccessResponse, extractScreenshotData, getEffectiveBuildId, isScreenshotEndpoint, validateScreenshotRequest } from './core.js';
9
+
10
+ // Operations - I/O with dependency injection
11
+ export { handleRequest, parseRequestBody, startServer, stopServer } from './operations.js';
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Screenshot Server Operations - I/O operations with dependency injection
3
+ *
4
+ * Each operation takes its dependencies as parameters for testability.
5
+ */
6
+
7
+ import { buildBadRequestResponse, buildInternalErrorResponse, buildNotFoundResponse, buildServerListenOptions, buildServerStartedMessage, buildServerStoppedMessage, buildSuccessResponse, extractScreenshotData, getEffectiveBuildId, isScreenshotEndpoint, validateScreenshotRequest } from './core.js';
8
+
9
+ // ============================================================================
10
+ // Request Body Parsing
11
+ // ============================================================================
12
+
13
+ /**
14
+ * Parse request body as JSON
15
+ * @param {Object} options - Options
16
+ * @param {Object} options.req - HTTP request
17
+ * @param {Object} options.deps - Dependencies
18
+ * @param {Function} options.deps.createError - Error factory
19
+ * @returns {Promise<Object>} Parsed body
20
+ */
21
+ export function parseRequestBody({
22
+ req,
23
+ deps
24
+ }) {
25
+ let {
26
+ createError
27
+ } = deps;
28
+ return new Promise((resolve, reject) => {
29
+ let body = '';
30
+ req.on('data', chunk => {
31
+ body += chunk.toString();
32
+ });
33
+ req.on('end', () => {
34
+ try {
35
+ resolve(JSON.parse(body));
36
+ } catch {
37
+ reject(createError('Invalid JSON in request body', 'INVALID_JSON'));
38
+ }
39
+ });
40
+ req.on('error', error => {
41
+ reject(createError(`Request error: ${error.message}`, 'REQUEST_ERROR'));
42
+ });
43
+ });
44
+ }
45
+
46
+ // ============================================================================
47
+ // Request Handling
48
+ // ============================================================================
49
+
50
+ /**
51
+ * Handle incoming HTTP request
52
+ * @param {Object} options - Options
53
+ * @param {Object} options.req - HTTP request
54
+ * @param {Object} options.res - HTTP response
55
+ * @param {Object} options.deps - Dependencies
56
+ * @param {Object} options.deps.buildManager - Build manager for screenshot storage
57
+ * @param {Function} options.deps.createError - Error factory
58
+ * @param {Object} options.deps.output - Output utilities
59
+ */
60
+ export async function handleRequest({
61
+ req,
62
+ res,
63
+ deps
64
+ }) {
65
+ let {
66
+ buildManager,
67
+ createError,
68
+ output
69
+ } = deps;
70
+
71
+ // Check if this is a screenshot endpoint
72
+ if (!isScreenshotEndpoint(req.method, req.url)) {
73
+ let response = buildNotFoundResponse();
74
+ sendResponse(res, response);
75
+ return;
76
+ }
77
+ try {
78
+ // Parse request body
79
+ let body = await parseRequestBody({
80
+ req,
81
+ deps: {
82
+ createError
83
+ }
84
+ });
85
+
86
+ // Validate required fields
87
+ let validation = validateScreenshotRequest(body);
88
+ if (!validation.valid) {
89
+ let response = buildBadRequestResponse(validation.error);
90
+ sendResponse(res, response);
91
+ return;
92
+ }
93
+
94
+ // Process screenshot
95
+ let effectiveBuildId = getEffectiveBuildId(body.buildId);
96
+ let screenshotData = extractScreenshotData(body);
97
+ await buildManager.addScreenshot(effectiveBuildId, screenshotData);
98
+ let response = buildSuccessResponse();
99
+ sendResponse(res, response);
100
+ } catch (error) {
101
+ output.error('Failed to process screenshot:', error);
102
+ let response = buildInternalErrorResponse();
103
+ sendResponse(res, response);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Send response to client
109
+ * @param {Object} res - HTTP response
110
+ * @param {{ status: number, body: Object }} response - Response data
111
+ */
112
+ function sendResponse(res, response) {
113
+ res.statusCode = response.status;
114
+ res.end(JSON.stringify(response.body));
115
+ }
116
+
117
+ // ============================================================================
118
+ // Server Lifecycle
119
+ // ============================================================================
120
+
121
+ /**
122
+ * Start the HTTP server
123
+ * @param {Object} options - Options
124
+ * @param {Object} options.config - Server configuration
125
+ * @param {Function} options.requestHandler - Request handler function
126
+ * @param {Object} options.deps - Dependencies
127
+ * @param {Function} options.deps.createHttpServer - HTTP server factory (http.createServer)
128
+ * @param {Function} options.deps.createError - Error factory
129
+ * @param {Object} options.deps.output - Output utilities
130
+ * @returns {Promise<Object>} HTTP server instance
131
+ */
132
+ export function startServer({
133
+ config,
134
+ requestHandler,
135
+ deps
136
+ }) {
137
+ let {
138
+ createHttpServer,
139
+ createError,
140
+ output
141
+ } = deps;
142
+ return new Promise((resolve, reject) => {
143
+ let server = createHttpServer(requestHandler);
144
+ let {
145
+ port,
146
+ host
147
+ } = buildServerListenOptions(config);
148
+ server.listen(port, host, error => {
149
+ if (error) {
150
+ reject(createError(`Failed to start screenshot server: ${error.message}`, 'SERVER_ERROR'));
151
+ } else {
152
+ output.info(buildServerStartedMessage(port));
153
+ resolve(server);
154
+ }
155
+ });
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Stop the HTTP server
161
+ * @param {Object} options - Options
162
+ * @param {Object|null} options.server - HTTP server instance
163
+ * @param {Object} options.deps - Dependencies
164
+ * @param {Object} options.deps.output - Output utilities
165
+ * @returns {Promise<void>}
166
+ */
167
+ export function stopServer({
168
+ server,
169
+ deps
170
+ }) {
171
+ let {
172
+ output
173
+ } = deps;
174
+ if (!server) {
175
+ return Promise.resolve();
176
+ }
177
+ return new Promise(resolve => {
178
+ server.close(() => {
179
+ output.info(buildServerStoppedMessage());
180
+ resolve();
181
+ });
182
+ });
183
+ }
package/dist/sdk/index.js CHANGED
@@ -13,8 +13,8 @@
13
13
  import { EventEmitter } from 'node:events';
14
14
  import { VizzlyError } from '../errors/vizzly-error.js';
15
15
  import { ScreenshotServer } from '../services/screenshot-server.js';
16
- import { createTDDService } from '../services/tdd-service.js';
17
16
  import { createUploader } from '../services/uploader.js';
17
+ import { createTDDService } from '../tdd/tdd-service.js';
18
18
  import { loadConfig } from '../utils/config-loader.js';
19
19
  import { resolveImageBuffer } from '../utils/file-helpers.js';
20
20
  import * as output from '../utils/output.js';
@@ -357,9 +357,10 @@ export class VizzlySDK extends EventEmitter {
357
357
  }
358
358
  }
359
359
  }
360
- export { createTDDService } from '../services/tdd-service.js';
360
+
361
361
  // Export service creators for advanced usage
362
362
  export { createUploader } from '../services/uploader.js';
363
+ export { createTDDService } from '../tdd/tdd-service.js';
363
364
  // Re-export key utilities and errors
364
365
  export { loadConfig } from '../utils/config-loader.js';
365
366
  export * as output from '../utils/output.js';
@@ -1,6 +1,7 @@
1
1
  import { Buffer } from 'node:buffer';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
+ import { uploadScreenshot as defaultUploadScreenshot } from '../../api/index.js';
4
5
  import { detectImageInputType } from '../../utils/image-input-detector.js';
5
6
  import * as output from '../../utils/output.js';
6
7
 
@@ -34,7 +35,15 @@ import * as output from '../../utils/output.js';
34
35
  * └─────────────────────────────────────────────────────────────┘
35
36
  */
36
37
 
37
- export const createApiHandler = apiService => {
38
+ /**
39
+ * Create an API handler for screenshot uploads.
40
+ * @param {Object} client - API client with request method
41
+ * @param {Object} options - Optional dependencies for testing
42
+ * @param {Function} options.uploadScreenshot - Upload function (defaults to API uploadScreenshot)
43
+ */
44
+ export const createApiHandler = (client, {
45
+ uploadScreenshot = defaultUploadScreenshot
46
+ } = {}) => {
38
47
  let vizzlyDisabled = false;
39
48
  let screenshotCount = 0;
40
49
  let uploadPromises = [];
@@ -52,13 +61,13 @@ export const createApiHandler = apiService => {
52
61
  };
53
62
  }
54
63
 
55
- // buildId is optional - API service will handle it appropriately
64
+ // buildId is optional - API will handle it appropriately
56
65
 
57
- if (!apiService) {
66
+ if (!client) {
58
67
  return {
59
68
  statusCode: 500,
60
69
  body: {
61
- error: 'API service not available'
70
+ error: 'API client not available'
62
71
  }
63
72
  };
64
73
  }
@@ -114,7 +123,7 @@ export const createApiHandler = apiService => {
114
123
  screenshotCount++;
115
124
 
116
125
  // Fire upload in background - DON'T AWAIT!
117
- const uploadPromise = apiService.uploadScreenshot(buildId, name, imageBuffer, properties ?? {}).then(result => {
126
+ let uploadPromise = uploadScreenshot(client, buildId, name, imageBuffer, properties ?? {}).then(result => {
118
127
  if (!result.skipped) {
119
128
  output.debug('upload', name);
120
129
  }