@vizzly-testing/cli 0.1.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.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +363 -0
  3. package/bin/vizzly.js +3 -0
  4. package/dist/cli.js +104 -0
  5. package/dist/client/index.js +237 -0
  6. package/dist/commands/doctor.js +158 -0
  7. package/dist/commands/init.js +102 -0
  8. package/dist/commands/run.js +224 -0
  9. package/dist/commands/status.js +164 -0
  10. package/dist/commands/tdd.js +212 -0
  11. package/dist/commands/upload.js +181 -0
  12. package/dist/container/index.js +184 -0
  13. package/dist/errors/vizzly-error.js +149 -0
  14. package/dist/index.js +31 -0
  15. package/dist/screenshot-wrapper.js +68 -0
  16. package/dist/sdk/index.js +364 -0
  17. package/dist/server/index.js +522 -0
  18. package/dist/services/api-service.js +215 -0
  19. package/dist/services/base-service.js +154 -0
  20. package/dist/services/build-manager.js +214 -0
  21. package/dist/services/screenshot-server.js +96 -0
  22. package/dist/services/server-manager.js +61 -0
  23. package/dist/services/service-utils.js +171 -0
  24. package/dist/services/tdd-service.js +444 -0
  25. package/dist/services/test-runner.js +210 -0
  26. package/dist/services/uploader.js +413 -0
  27. package/dist/types/cli.d.ts +2 -0
  28. package/dist/types/client/index.d.ts +76 -0
  29. package/dist/types/commands/doctor.d.ts +11 -0
  30. package/dist/types/commands/init.d.ts +14 -0
  31. package/dist/types/commands/run.d.ts +13 -0
  32. package/dist/types/commands/status.d.ts +13 -0
  33. package/dist/types/commands/tdd.d.ts +13 -0
  34. package/dist/types/commands/upload.d.ts +13 -0
  35. package/dist/types/container/index.d.ts +61 -0
  36. package/dist/types/errors/vizzly-error.d.ts +75 -0
  37. package/dist/types/index.d.ts +10 -0
  38. package/dist/types/index.js +153 -0
  39. package/dist/types/screenshot-wrapper.d.ts +27 -0
  40. package/dist/types/sdk/index.d.ts +108 -0
  41. package/dist/types/server/index.d.ts +38 -0
  42. package/dist/types/services/api-service.d.ts +77 -0
  43. package/dist/types/services/base-service.d.ts +72 -0
  44. package/dist/types/services/build-manager.d.ts +68 -0
  45. package/dist/types/services/screenshot-server.d.ts +10 -0
  46. package/dist/types/services/server-manager.d.ts +8 -0
  47. package/dist/types/services/service-utils.d.ts +45 -0
  48. package/dist/types/services/tdd-service.d.ts +55 -0
  49. package/dist/types/services/test-runner.d.ts +25 -0
  50. package/dist/types/services/uploader.d.ts +34 -0
  51. package/dist/types/types/index.d.ts +373 -0
  52. package/dist/types/utils/colors.d.ts +12 -0
  53. package/dist/types/utils/config-helpers.d.ts +6 -0
  54. package/dist/types/utils/config-loader.d.ts +22 -0
  55. package/dist/types/utils/console-ui.d.ts +61 -0
  56. package/dist/types/utils/diagnostics.d.ts +69 -0
  57. package/dist/types/utils/environment-config.d.ts +54 -0
  58. package/dist/types/utils/environment.d.ts +36 -0
  59. package/dist/types/utils/error-messages.d.ts +42 -0
  60. package/dist/types/utils/fetch-utils.d.ts +1 -0
  61. package/dist/types/utils/framework-detector.d.ts +5 -0
  62. package/dist/types/utils/git.d.ts +44 -0
  63. package/dist/types/utils/help.d.ts +11 -0
  64. package/dist/types/utils/image-comparison.d.ts +42 -0
  65. package/dist/types/utils/logger-factory.d.ts +26 -0
  66. package/dist/types/utils/logger.d.ts +79 -0
  67. package/dist/types/utils/package-info.d.ts +15 -0
  68. package/dist/types/utils/package.d.ts +1 -0
  69. package/dist/types/utils/project-detection.d.ts +19 -0
  70. package/dist/types/utils/ui-helpers.d.ts +23 -0
  71. package/dist/utils/colors.js +66 -0
  72. package/dist/utils/config-helpers.js +8 -0
  73. package/dist/utils/config-loader.js +120 -0
  74. package/dist/utils/console-ui.js +226 -0
  75. package/dist/utils/diagnostics.js +184 -0
  76. package/dist/utils/environment-config.js +93 -0
  77. package/dist/utils/environment.js +109 -0
  78. package/dist/utils/error-messages.js +34 -0
  79. package/dist/utils/fetch-utils.js +9 -0
  80. package/dist/utils/framework-detector.js +40 -0
  81. package/dist/utils/git.js +226 -0
  82. package/dist/utils/help.js +66 -0
  83. package/dist/utils/image-comparison.js +172 -0
  84. package/dist/utils/logger-factory.js +76 -0
  85. package/dist/utils/logger.js +231 -0
  86. package/dist/utils/package-info.js +38 -0
  87. package/dist/utils/package.js +9 -0
  88. package/dist/utils/project-detection.js +145 -0
  89. package/dist/utils/ui-helpers.js +86 -0
  90. package/package.json +103 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Stubborn Mule Software
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,363 @@
1
+ # Vizzly CLI
2
+
3
+ > Visual review platform for UI developers and designers
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@vizzly-testing/cli.svg)](https://www.npmjs.com/package/@vizzly-testing/cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## What is Vizzly?
9
+
10
+ Vizzly is a visual review platform designed for how modern teams work. Instead of recreating your components in a sandboxed environment, Vizzly captures screenshots directly from your functional tests. This means you test the *real thing*, not a snapshot.
11
+
12
+ It's fast because we don't render anything—we process the images you provide from any source. Bring screenshots from web apps, mobile apps, or even design mockups, and use our collaborative dashboard to streamline the review process between developers and designers.
13
+
14
+ ## Features
15
+
16
+ - 📸 **Smart Screenshots** - Automatic deduplication and intelligent diffing
17
+ - 🎨 **Any Screenshot** - Web, mobile, desktop, design mockups, or any visual content
18
+ - 🏃 **TDD Mode** - Local visual comparison for rapid development
19
+ - 📊 **Beautiful Dashboard** - Intuitive web interface for reviewing changes
20
+ - 👥 **Team Collaboration** - Built for UI developers and designers to work together
21
+ - 🔄 **CI/CD Ready** - GitHub, GitLab, CircleCI, and more
22
+
23
+ ## Quick Start
24
+
25
+ Requirements: Node.js 20 or newer.
26
+
27
+ ```bash
28
+ # Install globally
29
+ npm install -g @vizzly-testing/cli
30
+
31
+ # Initialize your project
32
+ vizzly init
33
+ ```
34
+
35
+ ### Set up your API token
36
+
37
+ For local development, create a `.env` file in your project root and add your token:
38
+
39
+ ```
40
+ VIZZLY_TOKEN=your-api-token
41
+ ```
42
+
43
+ Then add `.env` to your `.gitignore` file. For CI/CD, use your provider's secret management system.
44
+
45
+ ### Upload existing screenshots
46
+
47
+ ```bash
48
+ vizzly upload ./screenshots --build-name "Release v1.2.3"
49
+ ```
50
+
51
+ ### Integrate with your tests
52
+
53
+ ```bash
54
+ # Run tests with Vizzly integration
55
+ vizzly run "npm test"
56
+
57
+ # Use TDD mode for local development
58
+ vizzly run "npm test" --tdd
59
+ ```
60
+
61
+ ### In your test code
62
+
63
+ ```javascript
64
+ import { vizzlyScreenshot } from '@vizzly-testing/cli/client';
65
+
66
+ // Your test framework takes the screenshot
67
+ const screenshot = await page.screenshot();
68
+
69
+ // Send to Vizzly for review
70
+ await vizzlyScreenshot('homepage', screenshot, {
71
+ browser: 'chrome',
72
+ viewport: '1920x1080'
73
+ });
74
+ ```
75
+
76
+ > **Multi-Language Support**: Currently available as a JavaScript/Node.js SDK with Python, Ruby, and other language bindings coming soon. The client SDK is lightweight and simply POSTs screenshot data to the CLI for processing.
77
+
78
+ ## Commands
79
+
80
+ ### Upload Screenshots
81
+ ```bash
82
+ vizzly upload <directory> # Upload screenshots from directory
83
+ vizzly upload ./screenshots --wait # Wait for processing
84
+ ```
85
+
86
+ ### Run Tests with Integration
87
+ ```bash
88
+ vizzly run "npm test" # Run with Vizzly integration
89
+ vizzly run "npm test" --tdd # Local TDD mode
90
+ vizzly run "pytest" --port 3002 # Custom port
91
+ vizzly run "npm test" --wait # Wait for build completion
92
+ vizzly run "npm test" --eager # Create build immediately
93
+ vizzly run "npm test" --allow-no-token # Run without API token
94
+ ```
95
+
96
+ #### Run Command Options
97
+
98
+ **Server Configuration:**
99
+ - `--port <port>` - Port for screenshot server (default: 47392)
100
+ - `--timeout <ms>` - Server timeout in milliseconds (default: 30000)
101
+
102
+ **Build Configuration:**
103
+ - `-b, --build-name <name>` - Custom build name
104
+ - `--branch <branch>` - Git branch override
105
+ - `--commit <sha>` - Git commit SHA override
106
+ - `--message <msg>` - Commit message
107
+ - `--environment <env>` - Environment name (default: test)
108
+
109
+ **Processing Options:**
110
+ - `--wait` - Wait for build completion and exit with appropriate code
111
+ - `--eager` - Create build immediately (default: lazy creation)
112
+ - `--threshold <number>` - Comparison threshold (0-1, default: 0.01)
113
+ - `--batch-size <n>` - Upload batch size used with `--wait`
114
+ - `--upload-timeout <ms>` - Upload wait timeout in ms
115
+
116
+ **Development & Testing:**
117
+ - `--tdd` - Enable TDD mode with local comparisons
118
+ - `--allow-no-token` - Allow running without API token (useful for local development)
119
+ - `--token <token>` - API token override
120
+
121
+ **Baseline Configuration:**
122
+ - `--baseline-build <id>` - Use specific build as baseline for comparisons
123
+ - `--baseline-comparison <id>` - Use specific comparison as baseline
124
+
125
+ ### Setup and Status Commands
126
+ ```bash
127
+ vizzly init # Create vizzly.config.js with defaults
128
+ vizzly status <build-id> # Check build progress and results
129
+ vizzly status <build-id> --verbose # Detailed build information
130
+ vizzly status <build-id> --json # Machine-readable output
131
+ vizzly doctor # Fast local preflight (no network)
132
+ vizzly doctor --api # Include API connectivity checks
133
+ ```
134
+
135
+ #### Init Command
136
+ Creates a basic `vizzly.config.js` configuration file with sensible defaults. No interactive prompts - just generates a clean config you can customize.
137
+
138
+ ```bash
139
+ vizzly init # Create config file
140
+ vizzly init --force # Overwrite existing config
141
+ ```
142
+
143
+ #### Status Command
144
+ Check the progress and results of your builds. Shows comprehensive information including:
145
+ - Build status and progress
146
+ - Screenshot and comparison counts (new, changed, identical)
147
+ - Git branch and commit details
148
+ - Direct web dashboard link
149
+ - Timing and execution information
150
+
151
+ ```bash
152
+ # Basic status check
153
+ vizzly status abc123-def456-build-id
154
+
155
+ # Detailed information for debugging
156
+ vizzly status abc123-def456-build-id --verbose
157
+
158
+ # JSON output for CI/CD integration
159
+ vizzly status abc123-def456-build-id --json
160
+ ```
161
+
162
+ ### Doctor
163
+ - Purpose: Quickly validate your local setup without network calls by default.
164
+ - Checks: Node.js version (>= 20), `apiUrl` format, comparison `threshold`, effective `port` (default 47392).
165
+ - Optional: Add `--api` to verify connectivity using your `VIZZLY_TOKEN`.
166
+
167
+ Examples:
168
+ ```bash
169
+ # Local-only checks
170
+ vizzly doctor
171
+
172
+ # Include API connectivity
173
+ VIZZLY_TOKEN=your-token vizzly doctor --api
174
+
175
+ # JSON output for tooling
176
+ vizzly doctor --json
177
+ ```
178
+
179
+ ## TDD Mode
180
+
181
+ TDD mode enables fast local development by comparing screenshots locally without uploading to Vizzly:
182
+
183
+ ```bash
184
+ # First run - creates local baselines (no token needed)
185
+ npx vizzly tdd "npm test"
186
+
187
+ # Make changes and test - fails if visual differences detected
188
+ npx vizzly tdd "npm test"
189
+
190
+ # Accept changes as new baseline
191
+ npx vizzly tdd "npm test" --set-baseline
192
+ ```
193
+
194
+ - **🐻 Auto-baseline creation**: Creates baselines locally when none exist
195
+ - **🐻 No token required**: Works entirely offline for local development
196
+ - **🐻 Tests fail on differences**: Immediate feedback when visuals change
197
+ - **🐻 Accept changes**: Use `--set-baseline` to update baselines
198
+
199
+ ## Configuration
200
+
201
+ Create a `vizzly.config.js` file with `vizzly init` or manually:
202
+
203
+ ```javascript
204
+ export default {
205
+ // API configuration
206
+ // Set VIZZLY_TOKEN environment variable or uncomment and set here:
207
+ // apiToken: 'your-token-here',
208
+
209
+ // Screenshot configuration
210
+ screenshots: {
211
+ directory: './screenshots',
212
+ formats: ['png']
213
+ },
214
+
215
+ // Server configuration
216
+ server: {
217
+ port: 47392,
218
+ screenshotPath: '/screenshot'
219
+ },
220
+
221
+ // Comparison configuration
222
+ comparison: {
223
+ threshold: 0.1,
224
+ ignoreAntialiasing: true
225
+ },
226
+
227
+ // Upload configuration
228
+ upload: {
229
+ concurrency: 5,
230
+ timeout: 30000
231
+ }
232
+ };
233
+ ```
234
+
235
+ Run `vizzly init` to generate this file automatically with sensible defaults.
236
+
237
+ ## Config Reference
238
+
239
+ For the full configuration schema and CLI options, see docs/api-reference.md.
240
+
241
+ ## Framework Examples
242
+
243
+ ### Playwright
244
+ ```javascript
245
+ import { vizzlyScreenshot } from '@vizzly-testing/cli/client';
246
+
247
+ test('homepage test', async ({ page }) => {
248
+ await page.goto('/');
249
+ const screenshot = await page.screenshot();
250
+ await vizzlyScreenshot('homepage', screenshot, {
251
+ browser: 'chrome',
252
+ viewport: '1920x1080'
253
+ });
254
+ });
255
+ ```
256
+
257
+ ### Cypress
258
+ ```javascript
259
+ // cypress/support/commands.js
260
+ Cypress.Commands.add('vizzlyScreenshot', (name, properties = {}) => {
261
+ cy.screenshot(name, { capture: 'viewport' });
262
+ cy.readFile(`cypress/screenshots/${name}.png`, 'base64').then((imageBase64) => {
263
+ const imageBuffer = Buffer.from(imageBase64, 'base64');
264
+ return vizzlyScreenshot(name, imageBuffer, {
265
+ browser: Cypress.browser.name,
266
+ ...properties
267
+ });
268
+ });
269
+ });
270
+ ```
271
+
272
+ ## CI/CD Integration
273
+
274
+ For CI/CD pipelines, use the `--wait` flag to wait for visual comparison results and get appropriate exit codes:
275
+
276
+ ### GitHub Actions
277
+ ```yaml
278
+ - name: Visual Tests
279
+ run: npx vizzly run "npm test" --wait
280
+ env:
281
+ VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
282
+ ```
283
+
284
+ ### GitLab CI
285
+ ```yaml
286
+ visual-tests:
287
+ stage: test
288
+ image: node:20
289
+ script:
290
+ - npm ci
291
+ - npx vizzly run "npm test" --wait
292
+ variables:
293
+ VIZZLY_TOKEN: $VIZZLY_TOKEN
294
+ ```
295
+
296
+ The `--wait` flag ensures the process:
297
+ - Waits for all screenshots to be processed
298
+ - Exits with code `1` if visual differences are detected
299
+ - Exits with code `0` if all comparisons pass
300
+ - Allows your CI to fail appropriately when visual regressions occur
301
+
302
+ ## API Reference
303
+
304
+ ### `vizzlyScreenshot(name, imageBuffer, properties)`
305
+ Send a screenshot to Vizzly.
306
+ - `name` (string): Screenshot identifier
307
+ - `imageBuffer` (Buffer): Image data
308
+ - `properties` (object): Metadata for organization
309
+
310
+ ### `isVizzlyEnabled()`
311
+ Check if Vizzly is enabled in the current environment.
312
+
313
+ ## Documentation
314
+
315
+ - [Getting Started](./docs/getting-started.md)
316
+ - [Upload Command Guide](./docs/upload-command.md)
317
+ - [Test Integration Guide](./docs/test-integration.md)
318
+ - [TDD Mode Guide](./docs/tdd-mode.md)
319
+ - [API Reference](./docs/api-reference.md)
320
+ - [Doctor Command](./docs/doctor-command.md)
321
+
322
+ ## Environment Variables
323
+
324
+ - `VIZZLY_TOKEN`: API authentication token. Example: `export VIZZLY_TOKEN=your-token`.
325
+ - `VIZZLY_API_URL`: Override API base URL. Default: `https://vizzly.dev`.
326
+ - `VIZZLY_LOG_LEVEL`: Logger level. One of `debug`, `info`, `warn`, `error`. Example: `export VIZZLY_LOG_LEVEL=debug`.
327
+
328
+ ## Contributing
329
+
330
+ We welcome contributions! Whether you're fixing bugs, adding features, or improving documentation, your help makes Vizzly better for everyone.
331
+
332
+ ### Getting Started
333
+
334
+ 1. Fork the repository on [GitHub](https://github.com/vizzly/cli)
335
+ 2. Clone your fork locally: `git clone https://github.com/your-username/cli.git`
336
+ 3. Install dependencies: `npm install`
337
+ 4. Run tests to ensure everything works: `npm test`
338
+
339
+ ### Development Workflow
340
+
341
+ 1. Create a feature branch: `git checkout -b feature/your-feature-name`
342
+ 2. Make your changes and **add tests** for any new functionality
343
+ 3. Run the linter: `npm run lint`
344
+ 4. Run tests: `npm test`
345
+ 5. Commit your changes using [gitmoji](https://gitmoji.dev/) format: `git commit -m '✨ Add your feature'`
346
+ 6. Push to your fork: `git push origin feature/your-feature-name`
347
+ 7. Open a Pull Request
348
+
349
+ ### Reporting Issues
350
+
351
+ Found a bug or have a feature request? Please [open an issue](https://github.com/vizzly/cli/issues) with:
352
+
353
+ - A clear description of the problem or request
354
+ - Steps to reproduce (for bugs)
355
+ - Your environment details (OS, Node.js version, etc.)
356
+
357
+ ### Development Setup
358
+
359
+ The CLI is built with modern JavaScript and requires Node.js 20+ (LTS). See the development scripts in `package.json` for available commands.
360
+
361
+ ## License
362
+
363
+ MIT © Stubborn Mule Software
package/bin/vizzly.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import '../dist/cli.js';
package/dist/cli.js ADDED
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import { init } from './commands/init.js';
4
+ import { uploadCommand, validateUploadOptions } from './commands/upload.js';
5
+ import { runCommand, validateRunOptions } from './commands/run.js';
6
+ import { tddCommand, validateTddOptions } from './commands/tdd.js';
7
+ import { statusCommand, validateStatusOptions } from './commands/status.js';
8
+ import { doctorCommand, validateDoctorOptions } from './commands/doctor.js';
9
+ import { getPackageVersion } from './utils/package-info.js';
10
+ program.name('vizzly').description('Vizzly CLI for visual regression testing').version(getPackageVersion()).option('-c, --config <path>', 'Config file path').option('--token <token>', 'Vizzly API token').option('-v, --verbose', 'Verbose output').option('--json', 'Machine-readable output').option('--no-color', 'Disable colored output');
11
+ program.command('init').description('Initialize Vizzly in your project').option('--force', 'Overwrite existing configuration').action(async options => {
12
+ const globalOptions = program.opts();
13
+ await init({
14
+ ...globalOptions,
15
+ ...options
16
+ });
17
+ });
18
+ program.command('upload').description('Upload screenshots to Vizzly').argument('<path>', 'Path to screenshots directory or file').option('-b, --build-name <name>', 'Build name for grouping').option('-m, --metadata <json>', 'Additional metadata as JSON').option('--batch-size <n>', 'Upload batch size', v => parseInt(v, 10)).option('--upload-timeout <ms>', 'Upload timeout in milliseconds', v => parseInt(v, 10)).option('--branch <branch>', 'Git branch').option('--commit <sha>', 'Git commit SHA').option('--message <msg>', 'Commit message').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--token <token>', 'API token override').option('--wait', 'Wait for build completion').action(async (path, options) => {
19
+ const globalOptions = program.opts();
20
+
21
+ // Validate options
22
+ const validationErrors = validateUploadOptions(path, options);
23
+ if (validationErrors.length > 0) {
24
+ console.error('Validation errors:');
25
+ validationErrors.forEach(error => console.error(` - ${error}`));
26
+ process.exit(1);
27
+ }
28
+ await uploadCommand(path, options, globalOptions);
29
+ });
30
+ program.command('tdd').description('Run tests in TDD mode with local visual comparisons').argument('<command>', 'Test command to run').option('--port <port>', 'Port for screenshot server', '47392').option('--branch <branch>', 'Git branch override').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--token <token>', 'API token override').option('--timeout <ms>', 'Server timeout in milliseconds', '30000').option('--baseline-build <id>', 'Use specific build as baseline').option('--baseline-comparison <id>', 'Use specific comparison as baseline').option('--set-baseline', 'Accept current screenshots as new baseline (overwrites existing)').option('--allow-no-token', 'Allow running without API token (no baselines)').action(async (command, options) => {
31
+ const globalOptions = program.opts();
32
+
33
+ // Validate options
34
+ const validationErrors = validateTddOptions(command, options);
35
+ if (validationErrors.length > 0) {
36
+ console.error('Validation errors:');
37
+ validationErrors.forEach(error => console.error(` - ${error}`));
38
+ process.exit(1);
39
+ }
40
+ await tddCommand(command, options, globalOptions);
41
+ });
42
+ program.command('run').description('Run tests with Vizzly integration').argument('<command>', 'Test command to run').option('--tdd', 'Enable TDD mode with auto-reload').option('--port <port>', 'Port for screenshot server', '47392').option('-b, --build-name <name>', 'Custom build name').option('--branch <branch>', 'Git branch override').option('--commit <sha>', 'Git commit SHA').option('--message <msg>', 'Commit message').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', parseFloat).option('--token <token>', 'API token override').option('--wait', 'Wait for build completion').option('--timeout <ms>', 'Server timeout in milliseconds', '30000').option('--eager', 'Create build immediately (default: lazy)').option('--allow-no-token', 'Allow running without API token').option('--baseline-build <id>', 'Use specific build as baseline').option('--baseline-comparison <id>', 'Use specific comparison as baseline').action(async (command, options) => {
43
+ const globalOptions = program.opts();
44
+
45
+ // Forward --tdd flag to TDD command (shortcut)
46
+ if (options.tdd) {
47
+ // Forward to tdd command with appropriate options
48
+ const tddOptions = {
49
+ port: options.port,
50
+ branch: options.branch,
51
+ environment: options.environment,
52
+ threshold: options.threshold,
53
+ token: options.token,
54
+ timeout: options.timeout,
55
+ baselineBuild: options.baselineBuild,
56
+ baselineComparison: options.baselineComparison,
57
+ allowNoToken: options.allowNoToken
58
+ };
59
+
60
+ // Validate options using TDD validator
61
+ const validationErrors = validateTddOptions(command, tddOptions);
62
+ if (validationErrors.length > 0) {
63
+ console.error('Validation errors:');
64
+ validationErrors.forEach(error => console.error(` - ${error}`));
65
+ process.exit(1);
66
+ }
67
+ await tddCommand(command, tddOptions, globalOptions);
68
+ return;
69
+ }
70
+
71
+ // Validate options
72
+ const validationErrors = validateRunOptions(command, options);
73
+ if (validationErrors.length > 0) {
74
+ console.error('Validation errors:');
75
+ validationErrors.forEach(error => console.error(` - ${error}`));
76
+ process.exit(1);
77
+ }
78
+ await runCommand(command, options, globalOptions);
79
+ });
80
+ program.command('status').description('Check the status of a build').argument('<build-id>', 'Build ID to check status for').action(async (buildId, options) => {
81
+ const globalOptions = program.opts();
82
+
83
+ // Validate options
84
+ const validationErrors = validateStatusOptions(buildId, options);
85
+ if (validationErrors.length > 0) {
86
+ console.error('Validation errors:');
87
+ validationErrors.forEach(error => console.error(` - ${error}`));
88
+ process.exit(1);
89
+ }
90
+ await statusCommand(buildId, options, globalOptions);
91
+ });
92
+ program.command('doctor').description('Run diagnostics to check your environment and configuration').option('--api', 'Include API connectivity checks').action(async options => {
93
+ const globalOptions = program.opts();
94
+
95
+ // Validate options
96
+ const validationErrors = validateDoctorOptions(options);
97
+ if (validationErrors.length > 0) {
98
+ console.error('Validation errors:');
99
+ validationErrors.forEach(error => console.error(` - ${error}`));
100
+ process.exit(1);
101
+ }
102
+ await doctorCommand(options, globalOptions);
103
+ });
104
+ program.parse();