@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
@@ -1,99 +1,65 @@
1
1
  /**
2
2
  * Screenshot Server Service
3
3
  * Listens for and processes screenshots from the test runner
4
+ *
5
+ * This class is a thin wrapper around the functional operations in
6
+ * src/screenshot-server/. It maintains backwards compatibility while
7
+ * delegating to pure functions for testability.
4
8
  */
5
9
 
6
10
  import { createServer } from 'node:http';
7
11
  import { VizzlyError } from '../errors/vizzly-error.js';
12
+ import { handleRequest, parseRequestBody, startServer, stopServer } from '../screenshot-server/index.js';
8
13
  import * as output from '../utils/output.js';
9
14
  export class ScreenshotServer {
10
- constructor(config, buildManager) {
15
+ constructor(config, buildManager, options = {}) {
11
16
  this.config = config;
12
17
  this.buildManager = buildManager;
13
18
  this.server = null;
19
+
20
+ // Dependency injection for testing
21
+ this.deps = options.deps || {
22
+ createHttpServer: createServer,
23
+ output,
24
+ createError: (message, code) => new VizzlyError(message, code)
25
+ };
14
26
  }
15
27
  async start() {
16
- this.server = createServer(this.handleRequest.bind(this));
17
- return new Promise((resolve, reject) => {
18
- this.server.listen(this.config.server.port, '127.0.0.1', error => {
19
- if (error) {
20
- reject(new VizzlyError(`Failed to start screenshot server: ${error.message}`, 'SERVER_ERROR'));
21
- } else {
22
- output.info(`Screenshot server listening on http://127.0.0.1:${this.config.server.port}`);
23
- resolve();
24
- }
25
- });
28
+ this.server = await startServer({
29
+ config: this.config,
30
+ requestHandler: this.handleRequest.bind(this),
31
+ deps: {
32
+ createHttpServer: this.deps.createHttpServer,
33
+ createError: this.deps.createError,
34
+ output: this.deps.output
35
+ }
26
36
  });
27
37
  }
28
38
  async stop() {
29
- if (this.server) {
30
- return new Promise(resolve => {
31
- this.server.close(() => {
32
- output.info('Screenshot server stopped');
33
- resolve();
34
- });
35
- });
36
- }
39
+ await stopServer({
40
+ server: this.server,
41
+ deps: {
42
+ output: this.deps.output
43
+ }
44
+ });
37
45
  }
38
46
  async handleRequest(req, res) {
39
- if (req.method === 'POST' && req.url === '/screenshot') {
40
- try {
41
- const body = await this.parseRequestBody(req);
42
- const {
43
- buildId,
44
- name,
45
- image,
46
- properties
47
- } = body;
48
- if (!name || !image) {
49
- res.statusCode = 400;
50
- res.end(JSON.stringify({
51
- error: 'name and image are required'
52
- }));
53
- return;
54
- }
55
-
56
- // Use default buildId if none provided
57
- const effectiveBuildId = buildId || 'default';
58
- await this.buildManager.addScreenshot(effectiveBuildId, {
59
- name,
60
- image,
61
- properties
62
- });
63
- res.statusCode = 200;
64
- res.end(JSON.stringify({
65
- success: true
66
- }));
67
- } catch (error) {
68
- output.error('Failed to process screenshot:', error);
69
- res.statusCode = 500;
70
- res.end(JSON.stringify({
71
- error: 'Internal server error'
72
- }));
47
+ await handleRequest({
48
+ req,
49
+ res,
50
+ deps: {
51
+ buildManager: this.buildManager,
52
+ createError: this.deps.createError,
53
+ output: this.deps.output
73
54
  }
74
- } else {
75
- res.statusCode = 404;
76
- res.end(JSON.stringify({
77
- error: 'Not found'
78
- }));
79
- }
55
+ });
80
56
  }
81
57
  async parseRequestBody(req) {
82
- return new Promise((resolve, reject) => {
83
- let body = '';
84
- req.on('data', chunk => {
85
- body += chunk.toString();
86
- });
87
- req.on('end', () => {
88
- try {
89
- resolve(JSON.parse(body));
90
- } catch {
91
- reject(new VizzlyError('Invalid JSON in request body', 'INVALID_JSON'));
92
- }
93
- });
94
- req.on('error', error => {
95
- reject(new VizzlyError(`Request error: ${error.message}`, 'REQUEST_ERROR'));
96
- });
58
+ return parseRequestBody({
59
+ req,
60
+ deps: {
61
+ createError: this.deps.createError
62
+ }
97
63
  });
98
64
  }
99
65
  }
@@ -1,108 +1,71 @@
1
1
  /**
2
2
  * Server Manager Service
3
3
  * Manages the HTTP server with functional handlers
4
+ *
5
+ * This class is a thin wrapper around the functional operations in
6
+ * src/server-manager/. It maintains backwards compatibility while
7
+ * delegating to pure functions for testability.
4
8
  */
5
9
 
6
10
  import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
7
- import { join } from 'node:path';
11
+ import { createApiClient } from '../api/index.js';
8
12
  import { createApiHandler } from '../server/handlers/api-handler.js';
9
13
  import { createTddHandler } from '../server/handlers/tdd-handler.js';
10
14
  import { createHttpServer } from '../server/http-server.js';
15
+ import { buildServerInterface, getTddResults, startServer, stopServer } from '../server-manager/index.js';
11
16
  export class ServerManager {
12
17
  constructor(config, options = {}) {
13
18
  this.config = config;
14
19
  this.httpServer = null;
15
20
  this.handler = null;
16
21
  this.services = options.services || {};
22
+ this.tddMode = false;
23
+
24
+ // Dependency injection for testing - defaults to real implementations
25
+ this.deps = options.deps || {
26
+ createHttpServer,
27
+ createTddHandler,
28
+ createApiHandler,
29
+ createApiClient,
30
+ fs: {
31
+ mkdirSync,
32
+ writeFileSync,
33
+ existsSync,
34
+ unlinkSync
35
+ }
36
+ };
17
37
  }
18
38
  async start(buildId = null, tddMode = false, setBaseline = false) {
19
39
  this.buildId = buildId;
20
40
  this.tddMode = tddMode;
21
41
  this.setBaseline = setBaseline;
22
- const port = this.config?.server?.port || 47392;
23
- if (this.tddMode) {
24
- this.handler = createTddHandler(this.config, process.cwd(), this.config?.baselineBuildId, this.config?.baselineComparisonId, this.setBaseline);
25
- await this.handler.initialize();
26
- } else {
27
- const apiService = await this.createApiService();
28
- this.handler = createApiHandler(apiService);
29
- }
30
-
31
- // Pass buildId and tddService in services so http-server can use them
32
- const servicesWithExtras = {
33
- ...this.services,
34
- buildId: this.buildId,
35
- // Expose tddService for baseline download operations (TDD mode only)
36
- tddService: this.tddMode ? this.handler.tddService : null
37
- };
38
- this.httpServer = createHttpServer(port, this.handler, servicesWithExtras);
39
- if (this.httpServer) {
40
- await this.httpServer.start();
41
- }
42
-
43
- // Write server info to .vizzly/server.json for SDK discovery
44
- // This allows SDKs that can't access environment variables (like Swift/iOS)
45
- // to discover both the server port and current build ID
46
- try {
47
- const vizzlyDir = join(process.cwd(), '.vizzly');
48
- mkdirSync(vizzlyDir, {
49
- recursive: true
50
- });
51
- const serverFile = join(vizzlyDir, 'server.json');
52
- const serverInfo = {
53
- port: port.toString(),
54
- pid: process.pid,
55
- startTime: Date.now()
56
- };
57
-
58
- // Include buildId if we have one (for `vizzly run` mode)
59
- if (this.buildId) {
60
- serverInfo.buildId = this.buildId;
61
- }
62
- writeFileSync(serverFile, JSON.stringify(serverInfo, null, 2));
63
- } catch {
64
- // Non-fatal - SDK can still use health check or environment variables
65
- }
66
- }
67
- async createApiService() {
68
- if (!this.config.apiKey) return null;
69
- const {
70
- ApiService
71
- } = await import('./api-service.js');
72
- return new ApiService({
73
- ...this.config,
74
- command: 'run'
42
+ let result = await startServer({
43
+ config: this.config,
44
+ buildId,
45
+ tddMode,
46
+ setBaseline,
47
+ projectRoot: process.cwd(),
48
+ services: this.services,
49
+ deps: this.deps
75
50
  });
51
+ this.httpServer = result.httpServer;
52
+ this.handler = result.handler;
76
53
  }
77
54
  async stop() {
78
- if (this.httpServer) {
79
- await this.httpServer.stop();
80
- }
81
- if (this.handler?.cleanup) {
82
- try {
83
- this.handler.cleanup();
84
- } catch {
85
- // Don't throw - cleanup errors shouldn't fail the stop process
86
- }
87
- }
88
-
89
- // Clean up server.json so the client SDK doesn't try to connect to a dead server
90
- try {
91
- const serverFile = join(process.cwd(), '.vizzly', 'server.json');
92
- if (existsSync(serverFile)) {
93
- unlinkSync(serverFile);
94
- }
95
- } catch {
96
- // Non-fatal - cleanup errors shouldn't fail the stop process
97
- }
55
+ await stopServer({
56
+ httpServer: this.httpServer,
57
+ handler: this.handler,
58
+ projectRoot: process.cwd(),
59
+ deps: this.deps
60
+ });
98
61
  }
99
62
 
100
63
  // Expose server interface for compatibility
101
64
  get server() {
102
- return {
103
- getScreenshotCount: buildId => this.handler?.getScreenshotCount?.(buildId) || 0,
104
- finishBuild: buildId => this.httpServer?.finishBuild?.(buildId)
105
- };
65
+ return buildServerInterface({
66
+ handler: this.handler,
67
+ httpServer: this.httpServer
68
+ });
106
69
  }
107
70
 
108
71
  /**
@@ -110,7 +73,9 @@ export class ServerManager {
110
73
  * Only available in TDD mode after tests have run
111
74
  */
112
75
  async getTddResults() {
113
- if (!this.tddMode || !this.handler?.getResults) return null;
114
- return await this.handler.getResults();
76
+ return getTddResults({
77
+ tddMode: this.tddMode,
78
+ handler: this.handler
79
+ });
115
80
  }
116
81
  }