@vizzly-testing/cli 0.9.0 → 0.10.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.
- package/README.md +80 -23
- package/dist/cli.js +51 -2
- package/dist/client/index.js +0 -2
- package/dist/commands/init.js +1 -6
- package/dist/commands/tdd-daemon.js +77 -27
- package/dist/plugin-loader.js +183 -0
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +13 -13
- package/dist/types/client/index.d.ts +0 -2
- package/dist/types/commands/tdd-daemon.d.ts +6 -0
- package/dist/types/plugin-loader.d.ts +8 -0
- package/dist/types/utils/config-loader.d.ts +1 -1
- package/dist/types/utils/config-schema.d.ts +217 -0
- package/dist/utils/config-loader.js +23 -12
- package/dist/utils/config-schema.js +134 -0
- package/docs/api-reference.md +1 -2
- package/docs/plugins.md +496 -0
- package/docs/tdd-mode.md +10 -8
- package/package.json +7 -3
package/docs/plugins.md
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
# Vizzly Plugin System
|
|
2
|
+
|
|
3
|
+
The Vizzly CLI supports a powerful plugin system that allows you to extend its functionality with
|
|
4
|
+
custom commands. This enables community contributions and specialized integrations while keeping the
|
|
5
|
+
core CLI lean and focused.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
Plugins are JavaScript modules that export a simple registration function. The CLI automatically
|
|
10
|
+
discovers plugins from `node_modules/@vizzly-testing/*` or loads them explicitly from your config
|
|
11
|
+
file.
|
|
12
|
+
|
|
13
|
+
## Benefits
|
|
14
|
+
|
|
15
|
+
- **Zero Configuration** - Just `npm install` and the plugin is available
|
|
16
|
+
- **Shared Infrastructure** - Plugins get access to config, logger, and services
|
|
17
|
+
- **Independent Releases** - Plugins can iterate without requiring CLI updates
|
|
18
|
+
- **Smaller Core** - Keep the main CLI lean by moving optional features to plugins
|
|
19
|
+
- **Community Extensible** - Anyone can build and share plugins
|
|
20
|
+
|
|
21
|
+
## Using Plugins
|
|
22
|
+
|
|
23
|
+
### Official Plugins
|
|
24
|
+
|
|
25
|
+
Official Vizzly plugins are published under the `@vizzly-testing/*` scope and are automatically
|
|
26
|
+
discovered:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Install plugin
|
|
30
|
+
npm install @vizzly-testing/storybook
|
|
31
|
+
|
|
32
|
+
# Use immediately - no configuration needed!
|
|
33
|
+
vizzly storybook ./storybook-static
|
|
34
|
+
|
|
35
|
+
# Plugin commands appear in help
|
|
36
|
+
vizzly --help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Community Plugins
|
|
40
|
+
|
|
41
|
+
You can use community plugins or your own local plugins by configuring them explicitly:
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// vizzly.config.js
|
|
45
|
+
export default {
|
|
46
|
+
plugins: [
|
|
47
|
+
'./my-custom-plugin.js', // Local file path
|
|
48
|
+
'npm-package-name', // npm package name
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This is useful for:
|
|
54
|
+
- Local plugin development and testing
|
|
55
|
+
- Private/internal company plugins
|
|
56
|
+
- Community plugins not in the `@vizzly-testing` scope
|
|
57
|
+
- Custom workflow automation
|
|
58
|
+
|
|
59
|
+
## Creating a Plugin
|
|
60
|
+
|
|
61
|
+
### Basic Plugin Structure
|
|
62
|
+
|
|
63
|
+
A plugin is a JavaScript module that exports an object with `name` and a `register` function:
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
// my-plugin.js
|
|
67
|
+
export default {
|
|
68
|
+
name: 'my-plugin',
|
|
69
|
+
version: '1.0.0', // Optional but recommended
|
|
70
|
+
|
|
71
|
+
register(program, { config, logger, services }) {
|
|
72
|
+
// Register your command with Commander.js
|
|
73
|
+
program
|
|
74
|
+
.command('my-command <arg>')
|
|
75
|
+
.description('Description of my command')
|
|
76
|
+
.option('--option <value>', 'An option')
|
|
77
|
+
.action(async (arg, options) => {
|
|
78
|
+
logger.info(`Running my-command with ${arg}`);
|
|
79
|
+
|
|
80
|
+
// Access shared services if needed
|
|
81
|
+
let apiService = await services.get('apiService');
|
|
82
|
+
|
|
83
|
+
// Your command logic here
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Plugin Interface
|
|
90
|
+
|
|
91
|
+
#### Required Fields
|
|
92
|
+
|
|
93
|
+
- **`name`** (string) - Unique identifier for your plugin
|
|
94
|
+
- **`register`** (function) - Called during CLI initialization to register commands
|
|
95
|
+
|
|
96
|
+
#### Optional Fields
|
|
97
|
+
|
|
98
|
+
- **`version`** (string) - Plugin version (recommended for debugging and compatibility)
|
|
99
|
+
|
|
100
|
+
#### Register Function Parameters
|
|
101
|
+
|
|
102
|
+
The `register` function receives two arguments:
|
|
103
|
+
|
|
104
|
+
1. **`program`** - [Commander.js](https://github.com/tj/commander.js) program instance for registering commands
|
|
105
|
+
2. **`context`** - Object containing:
|
|
106
|
+
- `config` - Merged Vizzly configuration object
|
|
107
|
+
- `logger` - Shared logger instance with `.debug()`, `.info()`, `.warn()`, `.error()` methods
|
|
108
|
+
- `services` - Service container with access to internal Vizzly services
|
|
109
|
+
|
|
110
|
+
### Available Services
|
|
111
|
+
|
|
112
|
+
Plugins can access these services from the container:
|
|
113
|
+
|
|
114
|
+
- **`logger`** - Component logger for consistent output
|
|
115
|
+
- **`apiService`** - Vizzly API client for interacting with the platform
|
|
116
|
+
- **`uploader`** - Screenshot upload service
|
|
117
|
+
- **`buildManager`** - Build lifecycle management
|
|
118
|
+
- **`serverManager`** - Screenshot server management
|
|
119
|
+
- **`tddService`** - TDD mode services
|
|
120
|
+
- **`testRunner`** - Test execution service
|
|
121
|
+
|
|
122
|
+
Example accessing a service:
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
register(program, { config, logger, services }) {
|
|
126
|
+
program
|
|
127
|
+
.command('upload-screenshots <dir>')
|
|
128
|
+
.action(async (dir) => {
|
|
129
|
+
let uploader = await services.get('uploader');
|
|
130
|
+
await uploader.uploadScreenshots(screenshots);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Local Plugin Development
|
|
136
|
+
|
|
137
|
+
### Setup
|
|
138
|
+
|
|
139
|
+
1. Create your plugin file:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
mkdir -p plugins
|
|
143
|
+
touch plugins/my-plugin.js
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
2. Write your plugin:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
// plugins/my-plugin.js
|
|
150
|
+
export default {
|
|
151
|
+
name: 'my-plugin',
|
|
152
|
+
version: '1.0.0',
|
|
153
|
+
register(program, { config, logger }) {
|
|
154
|
+
program
|
|
155
|
+
.command('greet <name>')
|
|
156
|
+
.description('Greet someone')
|
|
157
|
+
.action((name) => {
|
|
158
|
+
logger.info(`Hello, ${name}!`);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
3. Configure Vizzly to load your plugin:
|
|
165
|
+
|
|
166
|
+
```javascript
|
|
167
|
+
// vizzly.config.js
|
|
168
|
+
export default {
|
|
169
|
+
plugins: ['./plugins/my-plugin.js'],
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
4. Test your plugin:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
vizzly --help # See your command listed
|
|
177
|
+
vizzly greet World # Test your command
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Package Structure (For Distribution)
|
|
181
|
+
|
|
182
|
+
If you want to share your plugin via npm:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
my-vizzly-plugin/
|
|
186
|
+
├── package.json
|
|
187
|
+
├── plugin.js # Main plugin file
|
|
188
|
+
├── lib/ # Plugin implementation
|
|
189
|
+
├── README.md
|
|
190
|
+
└── tests/
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Add a `vizzly.plugin` field to your `package.json`:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"name": "vizzly-plugin-custom",
|
|
198
|
+
"version": "1.0.0",
|
|
199
|
+
"description": "My custom Vizzly plugin",
|
|
200
|
+
"main": "./lib/index.js",
|
|
201
|
+
"vizzly": {
|
|
202
|
+
"plugin": "./plugin.js"
|
|
203
|
+
},
|
|
204
|
+
"keywords": ["vizzly", "vizzly-plugin"],
|
|
205
|
+
"peerDependencies": {
|
|
206
|
+
"@vizzly-testing/cli": "^0.9.0"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Users can then install and use your plugin:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
npm install vizzly-plugin-custom
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
// vizzly.config.js
|
|
219
|
+
export default {
|
|
220
|
+
plugins: ['vizzly-plugin-custom'],
|
|
221
|
+
};
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Best Practices
|
|
225
|
+
|
|
226
|
+
### Error Handling
|
|
227
|
+
|
|
228
|
+
Always handle errors gracefully and provide helpful error messages:
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
register(program, { logger }) {
|
|
232
|
+
program
|
|
233
|
+
.command('process <file>')
|
|
234
|
+
.action(async (file) => {
|
|
235
|
+
try {
|
|
236
|
+
if (!existsSync(file)) {
|
|
237
|
+
logger.error(`File not found: ${file}`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
// Process file...
|
|
241
|
+
} catch (error) {
|
|
242
|
+
logger.error(`Failed to process file: ${error.message}`);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Logging
|
|
250
|
+
|
|
251
|
+
Use the provided logger for consistent output across all CLI commands:
|
|
252
|
+
|
|
253
|
+
```javascript
|
|
254
|
+
logger.debug('Detailed debug info'); // Only shown with --verbose
|
|
255
|
+
logger.info('Normal information'); // Standard output
|
|
256
|
+
logger.warn('Warning message'); // Warning output
|
|
257
|
+
logger.error('Error message'); // Error output
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Async Operations
|
|
261
|
+
|
|
262
|
+
Use async/await for asynchronous operations:
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
.action(async (options) => {
|
|
266
|
+
let service = await services.get('apiService');
|
|
267
|
+
let result = await service.doSomething();
|
|
268
|
+
logger.info(`Result: ${result}`);
|
|
269
|
+
});
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Command Naming
|
|
273
|
+
|
|
274
|
+
Choose clear, descriptive command names that don't conflict with core commands:
|
|
275
|
+
|
|
276
|
+
✅ **Good command names:**
|
|
277
|
+
- `vizzly storybook`
|
|
278
|
+
- `vizzly import-chromatic`
|
|
279
|
+
- `vizzly generate-report`
|
|
280
|
+
|
|
281
|
+
❌ **Avoid these (conflicts with core):**
|
|
282
|
+
- `vizzly run`
|
|
283
|
+
- `vizzly upload`
|
|
284
|
+
- `vizzly init`
|
|
285
|
+
- `vizzly tdd`
|
|
286
|
+
|
|
287
|
+
### Input Validation
|
|
288
|
+
|
|
289
|
+
Validate user input and provide helpful error messages:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
.action(async (path, options) => {
|
|
293
|
+
if (!path) {
|
|
294
|
+
logger.error('Path is required');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!existsSync(path)) {
|
|
299
|
+
logger.error(`Path not found: ${path}`);
|
|
300
|
+
logger.info('Please provide a valid path to your build directory');
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Continue with logic
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Lazy Loading
|
|
309
|
+
|
|
310
|
+
Import heavy dependencies only when needed to keep CLI startup fast:
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
register(program, { logger }) {
|
|
314
|
+
program
|
|
315
|
+
.command('process-images <dir>')
|
|
316
|
+
.action(async (dir) => {
|
|
317
|
+
// Only import when command runs
|
|
318
|
+
let { processImages } = await import('./heavy-dependency.js');
|
|
319
|
+
await processImages(dir);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## Examples
|
|
325
|
+
|
|
326
|
+
### Simple Command Plugin
|
|
327
|
+
|
|
328
|
+
```javascript
|
|
329
|
+
export default {
|
|
330
|
+
name: 'hello',
|
|
331
|
+
version: '1.0.0',
|
|
332
|
+
register(program, { logger }) {
|
|
333
|
+
program
|
|
334
|
+
.command('hello <name>')
|
|
335
|
+
.description('Say hello')
|
|
336
|
+
.option('-l, --loud', 'Say it loudly')
|
|
337
|
+
.action((name, options) => {
|
|
338
|
+
let greeting = `Hello, ${name}!`;
|
|
339
|
+
if (options.loud) {
|
|
340
|
+
greeting = greeting.toUpperCase();
|
|
341
|
+
}
|
|
342
|
+
logger.info(greeting);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Screenshot Capture Plugin
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
export default {
|
|
352
|
+
name: 'storybook',
|
|
353
|
+
version: '1.0.0',
|
|
354
|
+
register(program, { config, logger, services }) {
|
|
355
|
+
program
|
|
356
|
+
.command('storybook <path>')
|
|
357
|
+
.description('Capture screenshots from Storybook build')
|
|
358
|
+
.option('--viewports <list>', 'Comma-separated viewports', '1280x720')
|
|
359
|
+
.action(async (path, options) => {
|
|
360
|
+
logger.info(`Crawling Storybook at ${path}`);
|
|
361
|
+
|
|
362
|
+
// Import dependencies lazily
|
|
363
|
+
let { crawlStorybook } = await import('./crawler.js');
|
|
364
|
+
|
|
365
|
+
// Capture screenshots
|
|
366
|
+
let screenshots = await crawlStorybook(path, {
|
|
367
|
+
viewports: options.viewports.split(','),
|
|
368
|
+
logger,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
logger.info(`Captured ${screenshots.length} screenshots`);
|
|
372
|
+
|
|
373
|
+
// Upload using Vizzly's uploader service
|
|
374
|
+
let uploader = await services.get('uploader');
|
|
375
|
+
await uploader.uploadScreenshots(screenshots);
|
|
376
|
+
|
|
377
|
+
logger.info('Upload complete!');
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Multi-Command Plugin
|
|
384
|
+
|
|
385
|
+
```javascript
|
|
386
|
+
export default {
|
|
387
|
+
name: 'reports',
|
|
388
|
+
version: '1.0.0',
|
|
389
|
+
register(program, { logger }) {
|
|
390
|
+
let reports = program
|
|
391
|
+
.command('reports')
|
|
392
|
+
.description('Report generation commands');
|
|
393
|
+
|
|
394
|
+
reports
|
|
395
|
+
.command('generate')
|
|
396
|
+
.description('Generate a new report')
|
|
397
|
+
.action(() => {
|
|
398
|
+
logger.info('Generating report...');
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
reports
|
|
402
|
+
.command('list')
|
|
403
|
+
.description('List all reports')
|
|
404
|
+
.action(() => {
|
|
405
|
+
logger.info('Listing reports...');
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Troubleshooting
|
|
412
|
+
|
|
413
|
+
### Plugin Not Loading
|
|
414
|
+
|
|
415
|
+
**Check plugin configuration:**
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
vizzly --verbose --help
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
This will show debug output about plugin loading.
|
|
422
|
+
|
|
423
|
+
**Common issues:**
|
|
424
|
+
- File path is incorrect (should be relative to `vizzly.config.js`)
|
|
425
|
+
- Plugin file has syntax errors
|
|
426
|
+
- Missing `export default` statement
|
|
427
|
+
|
|
428
|
+
### Plugin Command Not Showing
|
|
429
|
+
|
|
430
|
+
**Verify plugin loaded successfully:**
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
vizzly --verbose --help 2>&1 | grep -i plugin
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Common issues:**
|
|
437
|
+
- Missing `name` or `register` function
|
|
438
|
+
- Command name conflicts with existing commands
|
|
439
|
+
- Plugin threw an error during registration
|
|
440
|
+
|
|
441
|
+
### Type Errors in Editor
|
|
442
|
+
|
|
443
|
+
If you're using TypeScript or want better IDE support, you can add JSDoc types:
|
|
444
|
+
|
|
445
|
+
```javascript
|
|
446
|
+
/**
|
|
447
|
+
* @param {import('commander').Command} program
|
|
448
|
+
* @param {Object} context
|
|
449
|
+
* @param {Object} context.config
|
|
450
|
+
* @param {Object} context.logger
|
|
451
|
+
* @param {Object} context.services
|
|
452
|
+
*/
|
|
453
|
+
function register(program, { config, logger, services }) {
|
|
454
|
+
// Your plugin code with full autocomplete!
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export default {
|
|
458
|
+
name: 'my-plugin',
|
|
459
|
+
register,
|
|
460
|
+
};
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Contributing a Plugin
|
|
464
|
+
|
|
465
|
+
### Share Your Plugin
|
|
466
|
+
|
|
467
|
+
Built a useful plugin? We'd love to feature it! Here's how to share:
|
|
468
|
+
|
|
469
|
+
1. **Publish to npm** (optional but recommended for reusability)
|
|
470
|
+
2. **Add documentation** - Include a clear README with usage examples
|
|
471
|
+
3. **Open an issue** on the [Vizzly CLI repository](https://github.com/vizzly-testing/cli/issues) with:
|
|
472
|
+
- Plugin name and description
|
|
473
|
+
- Link to repository or npm package
|
|
474
|
+
- Usage example
|
|
475
|
+
- Screenshots/demo if applicable
|
|
476
|
+
|
|
477
|
+
### Plugin Ideas
|
|
478
|
+
|
|
479
|
+
Here are some plugin ideas the community might find useful:
|
|
480
|
+
|
|
481
|
+
- **Playwright integration** - Automated screenshot capture from Playwright tests
|
|
482
|
+
- **Cypress integration** - Screenshot capture from Cypress tests
|
|
483
|
+
- **Percy migration** - Import screenshots from Percy
|
|
484
|
+
- **Chromatic migration** - Import screenshots from Chromatic
|
|
485
|
+
- **Figma comparison** - Compare designs with Figma files
|
|
486
|
+
- **Image optimization** - Compress screenshots before upload
|
|
487
|
+
- **Custom reporters** - Generate custom HTML/PDF reports
|
|
488
|
+
- **CI/CD integrations** - Specialized workflows for different CI platforms
|
|
489
|
+
|
|
490
|
+
## Support
|
|
491
|
+
|
|
492
|
+
Need help building a plugin?
|
|
493
|
+
|
|
494
|
+
- 📖 [View example plugin source](https://github.com/vizzly-testing/cli/tree/main/examples/custom-plugin)
|
|
495
|
+
- 🐛 [Report issues](https://github.com/vizzly-testing/cli/issues)
|
|
496
|
+
- 📧 [Email support](https://vizzly.dev/support/)
|
package/docs/tdd-mode.md
CHANGED
|
@@ -26,11 +26,11 @@ npx vizzly tdd start
|
|
|
26
26
|
🐻 **Dashboard starts:**
|
|
27
27
|
- Opens at `http://localhost:47392` (or custom `--port`)
|
|
28
28
|
- Shows empty state ready for comparisons
|
|
29
|
-
- Runs in
|
|
29
|
+
- Runs in the background and returns your terminal immediately
|
|
30
30
|
|
|
31
31
|
### 2. Run Your Tests in Watch Mode
|
|
32
32
|
|
|
33
|
-
In a
|
|
33
|
+
In the same terminal or a new one, run your tests in watch mode:
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
npm test -- --watch
|
|
@@ -204,11 +204,13 @@ open .vizzly/report/index.html # macOS
|
|
|
204
204
|
vizzly tdd start [options]
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
+
Starts the dashboard server in the background as a detached process and returns your terminal immediately.
|
|
208
|
+
|
|
207
209
|
Options:
|
|
208
210
|
- `--port <port>` - Server port (default: 47392)
|
|
209
211
|
- `--threshold <number>` - Comparison threshold (default: 0.1)
|
|
210
212
|
- `--baseline-build <id>` - Use specific build as baseline
|
|
211
|
-
- `--
|
|
213
|
+
- `--open` - Auto-open dashboard in browser
|
|
212
214
|
|
|
213
215
|
**Run Tests (Single-Shot)**
|
|
214
216
|
```bash
|
|
@@ -240,17 +242,17 @@ vizzly tdd "npm test" # Equivalent to: vizzly tdd run "npm test"
|
|
|
240
242
|
### Interactive Development (Recommended)
|
|
241
243
|
|
|
242
244
|
```bash
|
|
243
|
-
#
|
|
245
|
+
# Start dashboard (runs in background)
|
|
244
246
|
npx vizzly tdd start
|
|
245
247
|
|
|
246
|
-
#
|
|
248
|
+
# Run tests in watch mode (same terminal or new one)
|
|
247
249
|
npm test -- --watch
|
|
248
250
|
|
|
249
251
|
# Browser: Open http://localhost:47392
|
|
250
252
|
# See live comparisons as you code
|
|
251
253
|
|
|
252
254
|
# Accept changes from dashboard UI when ready
|
|
253
|
-
#
|
|
255
|
+
# Stop when done: npx vizzly tdd stop
|
|
254
256
|
```
|
|
255
257
|
|
|
256
258
|
### Single-Shot Testing
|
|
@@ -269,10 +271,10 @@ npx vizzly run "npm test" --wait
|
|
|
269
271
|
### Feature Development
|
|
270
272
|
|
|
271
273
|
```bash
|
|
272
|
-
# Start interactive dashboard
|
|
274
|
+
# Start interactive dashboard (runs in background)
|
|
273
275
|
npx vizzly tdd start
|
|
274
276
|
|
|
275
|
-
#
|
|
277
|
+
# Run tests in watch mode (same terminal or new one)
|
|
276
278
|
npm test -- --watch
|
|
277
279
|
|
|
278
280
|
# Make changes and see live feedback in browser
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizzly-testing/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Visual review platform for UI developers and designers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"visual-testing",
|
|
@@ -63,6 +63,8 @@
|
|
|
63
63
|
"prepublishOnly": "npm test && npm run build",
|
|
64
64
|
"test": "vitest run",
|
|
65
65
|
"test:watch": "vitest",
|
|
66
|
+
"test:reporter": "playwright test --config=tests/reporter/playwright.config.js",
|
|
67
|
+
"test:reporter:visual": "node bin/vizzly.js run \"npm run test:reporter\"",
|
|
66
68
|
"lint": "eslint src tests",
|
|
67
69
|
"lint:fix": "eslint src tests --fix",
|
|
68
70
|
"format": "prettier --write src tests",
|
|
@@ -81,7 +83,8 @@
|
|
|
81
83
|
"dotenv": "^17.2.1",
|
|
82
84
|
"form-data": "^4.0.0",
|
|
83
85
|
"glob": "^11.0.3",
|
|
84
|
-
"odiff-bin": "^3.2.1"
|
|
86
|
+
"odiff-bin": "^3.2.1",
|
|
87
|
+
"zod": "^3.24.1"
|
|
85
88
|
},
|
|
86
89
|
"devDependencies": {
|
|
87
90
|
"@babel/cli": "^7.28.0",
|
|
@@ -91,6 +94,7 @@
|
|
|
91
94
|
"@babel/preset-typescript": "^7.23.6",
|
|
92
95
|
"@eslint/js": "^9.31.0",
|
|
93
96
|
"@heroicons/react": "^2.2.0",
|
|
97
|
+
"@playwright/test": "^1.55.1",
|
|
94
98
|
"@tailwindcss/postcss": "^4.1.13",
|
|
95
99
|
"@vitejs/plugin-react": "^5.0.3",
|
|
96
100
|
"@vitest/coverage-v8": "^3.2.4",
|
|
@@ -100,7 +104,7 @@
|
|
|
100
104
|
"eslint-config-prettier": "^10.1.8",
|
|
101
105
|
"eslint-plugin-prettier": "^5.5.3",
|
|
102
106
|
"eslint-plugin-react": "^7.37.5",
|
|
103
|
-
"eslint-plugin-react-hooks": "^
|
|
107
|
+
"eslint-plugin-react-hooks": "^6.1.1",
|
|
104
108
|
"postcss": "^8.5.6",
|
|
105
109
|
"prettier": "^3.6.2",
|
|
106
110
|
"react": "^19.1.1",
|