@vizzly-testing/cli 0.31.0 → 0.33.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 +67 -50
- package/dist/cli.js +8 -8
- package/dist/client/index.js +2 -1
- package/dist/commands/doctor.js +4 -4
- package/dist/commands/init.js +1 -1
- package/dist/commands/preview.js +3 -3
- package/dist/commands/run.js +1 -1
- package/dist/commands/tdd-daemon.js +1 -1
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +118 -105
- package/dist/reporter-ssr/ssr-entry.js +88 -88
- package/dist/services/static-report-generator.js +2 -2
- package/dist/tdd/core/region-coverage.js +109 -21
- package/dist/tdd/services/comparison-service.js +10 -1
- package/dist/tdd/tdd-service.js +76 -39
- package/dist/utils/colors.js +28 -45
- package/dist/utils/global-config.js +1 -1
- package/dist/utils/output.js +16 -16
- package/dist/utils/screenshot-options.js +1 -0
- package/docs/assets/vizzly-mascot-inspector.png +0 -0
- package/package.json +13 -11
package/README.md
CHANGED
|
@@ -1,70 +1,73 @@
|
|
|
1
1
|
# Vizzly CLI
|
|
2
2
|
|
|
3
|
-
> Visual
|
|
3
|
+
> Visual regression testing from your terminal
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@vizzly-testing/cli)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
<p align="center">
|
|
9
|
+
<img src="./docs/assets/vizzly-mascot-inspector.png" alt="Vizzly bear inspecting a screenshot" width="220" />
|
|
10
|
+
</p>
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
Vizzly is a visual testing and regression platform for teams that ship UI. It
|
|
13
|
+
captures screenshots from your real tests, compares them to approved baselines,
|
|
14
|
+
and gives you useful review data: meaningful diffs, comments, approvals, build
|
|
15
|
+
status, and preview links.
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
**Meaningful diff metadata.** Vizzly stores rich diff evidence: changed regions, cluster metadata, fingerprints, hotspots, confirmed regions, and image URLs. Agents can inspect what changed instead of guessing from a pass/fail label.
|
|
17
|
-
|
|
18
|
-
**Any screenshot source.** Playwright, Cypress, Puppeteer, Selenium, native mobile apps, or even design mockups. If you can capture it, Vizzly can compare it.
|
|
19
|
-
|
|
20
|
-
**Approved baselines as truth.** Cloud context carries human review state. Local context carries the downloaded or generated baseline metadata. That is the bridge between TDD locally and collaborative review in Vizzly.
|
|
17
|
+
This package is the CLI and local SDK surface. Use it to run local visual TDD,
|
|
18
|
+
upload screenshots from CI, generate static reports, and fetch machine-readable
|
|
19
|
+
build or diff data for scripts, automation, and coding agents.
|
|
21
20
|
|
|
22
21
|
## Quick Start
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
You need Node.js 22+.
|
|
25
24
|
|
|
26
25
|
```bash
|
|
27
|
-
|
|
26
|
+
pnpm install -g @vizzly-testing/cli
|
|
28
27
|
|
|
29
28
|
vizzly init
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
### Local
|
|
31
|
+
### Start Local TDD
|
|
33
32
|
|
|
34
|
-
Start the TDD server, run your tests
|
|
33
|
+
Start the TDD server, run your tests, and open the dashboard at
|
|
34
|
+
`http://localhost:47392`.
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
vizzly tdd start
|
|
38
|
-
|
|
38
|
+
pnpm test -- --watch
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
The dashboard shows diffs
|
|
41
|
+
The dashboard shows screenshots, baselines, and diffs as they arrive. Accept or
|
|
42
|
+
reject changes right from the UI.
|
|
42
43
|
|
|
43
|
-
### Cloud
|
|
44
|
+
### Run With Cloud Review
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
Use cloud builds when you want shared baselines, team review, and CI status:
|
|
46
47
|
|
|
47
48
|
```bash
|
|
48
49
|
vizzly login
|
|
49
|
-
vizzly run "
|
|
50
|
+
vizzly run "pnpm test" --wait
|
|
50
51
|
```
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
`--wait` blocks until Vizzly finishes processing the build. It exits with code
|
|
54
|
+
`1` when visual differences need review.
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
In CI, use a project token:
|
|
55
57
|
|
|
56
58
|
```bash
|
|
57
59
|
export VIZZLY_TOKEN=your-project-token
|
|
58
|
-
vizzly run "
|
|
60
|
+
vizzly run "pnpm test" --wait
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
###
|
|
63
|
+
### Inspect Builds And Diffs
|
|
62
64
|
|
|
63
|
-
Use `vizzly context` when you
|
|
65
|
+
Use `vizzly context` when you need build, comparison, screenshot, or review
|
|
66
|
+
queue data from the terminal.
|
|
64
67
|
|
|
65
|
-
This is
|
|
66
|
-
making a
|
|
67
|
-
|
|
68
|
+
This is useful for scripts, coding agents, and debugging loops. Instead of
|
|
69
|
+
making a pile of narrow API calls, ask for one focused bundle and get the
|
|
70
|
+
evidence in one place.
|
|
68
71
|
|
|
69
72
|
```bash
|
|
70
73
|
# Cloud context for a build or comparison
|
|
@@ -75,15 +78,19 @@ vizzly context comparison def456 --json
|
|
|
75
78
|
vizzly context build current --source local
|
|
76
79
|
vizzly context build current --source local --agent
|
|
77
80
|
vizzly context screenshot build-detail-screenshots --source local --json
|
|
81
|
+
vizzly context review-queue --source local --json
|
|
78
82
|
```
|
|
79
83
|
|
|
80
|
-
`--json` is the durable automation path. `--agent` gives a compact handoff for
|
|
84
|
+
`--json` is the durable automation path. `--agent` gives a compact handoff for
|
|
85
|
+
prompt assembly. Add `--full` when you need the whole payload, or
|
|
86
|
+
`--include screenshots,diffs,comments` when compact JSON needs selected detail.
|
|
81
87
|
|
|
82
|
-
Local context is read-only and file-backed. It reads your existing `.vizzly`
|
|
83
|
-
TDD runs, including screenshots, diffs, and
|
|
88
|
+
Local context is read-only and file-backed. It reads your existing `.vizzly`
|
|
89
|
+
workspace state from TDD runs, including screenshots, diffs, and saved hotspot
|
|
90
|
+
or region metadata.
|
|
84
91
|
|
|
85
|
-
Cloud context is also read-only right now. That is intentional.
|
|
86
|
-
|
|
92
|
+
Cloud context is also read-only right now. That is intentional. Vizzly helps you
|
|
93
|
+
see and inspect visual changes, while people still decide what gets approved.
|
|
87
94
|
|
|
88
95
|
## Capture Screenshots
|
|
89
96
|
|
|
@@ -103,7 +110,14 @@ test('homepage looks correct', async ({ page }) => {
|
|
|
103
110
|
});
|
|
104
111
|
```
|
|
105
112
|
|
|
106
|
-
The client SDK is lightweight
|
|
113
|
+
The client SDK is lightweight. It posts screenshots to the local Vizzly server
|
|
114
|
+
or the cloud build wrapper. It works with any test runner.
|
|
115
|
+
|
|
116
|
+
SDKs are available for
|
|
117
|
+
[JavaScript](https://docs.vizzly.dev/integration/sdk/javascript),
|
|
118
|
+
[Ruby](https://docs.vizzly.dev/integration/sdk/ruby),
|
|
119
|
+
[Swift](https://docs.vizzly.dev/integration/sdk/swift), and
|
|
120
|
+
[more](https://docs.vizzly.dev/integration/sdk/overview).
|
|
107
121
|
|
|
108
122
|
Already saving screenshots to disk? Pass the file path instead:
|
|
109
123
|
|
|
@@ -131,31 +145,34 @@ Or create `vizzly.config.js` manually:
|
|
|
131
145
|
```javascript
|
|
132
146
|
export default {
|
|
133
147
|
comparison: {
|
|
134
|
-
|
|
135
|
-
|
|
148
|
+
// CIEDE2000 Delta E. 0 is exact. 2 is a good default.
|
|
149
|
+
threshold: 2.0,
|
|
150
|
+
},
|
|
136
151
|
};
|
|
137
152
|
```
|
|
138
153
|
|
|
139
154
|
## Commands
|
|
140
155
|
|
|
141
|
-
| Command |
|
|
142
|
-
|
|
143
|
-
| `vizzly tdd start` | Start local TDD server
|
|
144
|
-
| `vizzly tdd run "cmd"` | Run tests once
|
|
145
|
-
| `vizzly run "cmd"` | Run tests with cloud integration |
|
|
146
|
-
| `vizzly context ...` | Fetch visual context
|
|
147
|
-
| `vizzly upload <dir>` | Upload existing screenshots |
|
|
148
|
-
| `vizzly login` | Authenticate
|
|
149
|
-
| `vizzly doctor` | Validate local setup |
|
|
156
|
+
| Command | What it does |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| `vizzly tdd start` | Start the local TDD server and dashboard. |
|
|
159
|
+
| `vizzly tdd run "cmd"` | Run tests once and generate a static visual report. |
|
|
160
|
+
| `vizzly run "cmd"` | Run tests with cloud build and review integration. |
|
|
161
|
+
| `vizzly context ...` | Fetch visual context for builds, comparisons, screenshots, and review queues. |
|
|
162
|
+
| `vizzly upload <dir>` | Upload an existing folder of screenshots. |
|
|
163
|
+
| `vizzly login` | Authenticate through the browser. |
|
|
164
|
+
| `vizzly doctor` | Validate your local setup. |
|
|
150
165
|
|
|
151
166
|
## Documentation
|
|
152
167
|
|
|
153
|
-
Full documentation at **[docs.vizzly.dev](https://docs.vizzly.dev)
|
|
154
|
-
CI
|
|
168
|
+
Full documentation lives at **[docs.vizzly.dev](https://docs.vizzly.dev)**.
|
|
169
|
+
Start there for framework guides, CI setup, SDK examples, and the configuration
|
|
170
|
+
reference.
|
|
155
171
|
|
|
156
172
|
## Contributing
|
|
157
173
|
|
|
158
|
-
Found a bug
|
|
174
|
+
Found a bug or have an idea?
|
|
175
|
+
[Open an issue](https://github.com/vizzly-testing/cli/issues) or send a PR.
|
|
159
176
|
|
|
160
177
|
## License
|
|
161
178
|
|
package/dist/cli.js
CHANGED
|
@@ -36,7 +36,7 @@ import { saveUserPath } from './utils/global-config.js';
|
|
|
36
36
|
import * as output from './utils/output.js';
|
|
37
37
|
import { getPackageVersion } from './utils/package-info.js';
|
|
38
38
|
|
|
39
|
-
// Custom help formatting with
|
|
39
|
+
// Custom help formatting with BearDen design system
|
|
40
40
|
const formatHelp = (cmd, helper) => {
|
|
41
41
|
let c = colors;
|
|
42
42
|
let lines = [];
|
|
@@ -49,7 +49,7 @@ const formatHelp = (cmd, helper) => {
|
|
|
49
49
|
// Cute grizzly bear mascot with square eyes (like the Vizzly logo!)
|
|
50
50
|
lines.push(c.brand.amber(' ʕ□ᴥ□ʔ'));
|
|
51
51
|
lines.push(` ${c.brand.amber(c.bold('vizzly'))} ${c.dim(`v${version}`)}`);
|
|
52
|
-
lines.push(` ${c.gray('Visual
|
|
52
|
+
lines.push(` ${c.gray('Visual regression testing from your terminal')}`);
|
|
53
53
|
} else {
|
|
54
54
|
// Compact header for subcommands
|
|
55
55
|
lines.push(` ${c.brand.amber(c.bold('vizzly'))} ${c.white(cmd.name())}`);
|
|
@@ -177,7 +177,7 @@ const formatHelp = (cmd, helper) => {
|
|
|
177
177
|
lines.push(` ${c.gray('$')} ${c.white('vizzly tdd start')}`);
|
|
178
178
|
lines.push('');
|
|
179
179
|
lines.push(` ${c.dim('# CI pipeline')}`);
|
|
180
|
-
lines.push(` ${c.gray('$')} ${c.white('vizzly run "
|
|
180
|
+
lines.push(` ${c.gray('$')} ${c.white('vizzly run "pnpm test" --wait')}`);
|
|
181
181
|
lines.push('');
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -252,7 +252,7 @@ function normalizeJsonArgv(argv, commandNames) {
|
|
|
252
252
|
}
|
|
253
253
|
return normalizedArgv;
|
|
254
254
|
}
|
|
255
|
-
program.name('vizzly').description('Vizzly CLI for visual
|
|
255
|
+
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 (shorthand for --log-level debug)').option('--log-level <level>', 'Log level: debug, info, warn, error (default: info, or VIZZLY_LOG_LEVEL env var)').option('--json [fields]', 'JSON output, optionally specify fields (e.g., --json id,status,branch)').option('--color', 'Force colored output (even in non-TTY)').option('--no-color', 'Disable colored output').option('--strict', 'Fail on any error (default: be resilient, warn on non-critical issues)').configureHelp({
|
|
256
256
|
formatHelp
|
|
257
257
|
});
|
|
258
258
|
|
|
@@ -411,7 +411,7 @@ tddCmd.command('run <command>').description('Run tests once in TDD mode with loc
|
|
|
411
411
|
const reportResult = await generateStaticReport(process.cwd());
|
|
412
412
|
if (reportResult.success) {
|
|
413
413
|
const reportUrl = getReportFileUrl(reportResult.reportPath);
|
|
414
|
-
output.print(` ${colors.brand.textTertiary('→')} Report: ${colors.
|
|
414
|
+
output.print(` ${colors.brand.textTertiary('→')} Report: ${colors.info(reportUrl)}`);
|
|
415
415
|
output.blank();
|
|
416
416
|
|
|
417
417
|
// Open report in browser unless --no-open
|
|
@@ -512,8 +512,8 @@ Examples:
|
|
|
512
512
|
}
|
|
513
513
|
await comparisonsCommand(options, globalOptions);
|
|
514
514
|
});
|
|
515
|
-
let contextCmd = program.command('context').description('Fetch build
|
|
516
|
-
contextCmd.command('build').description('Fetch build context
|
|
515
|
+
let contextCmd = program.command('context').description('Fetch build and diff context');
|
|
516
|
+
contextCmd.command('build').description('Fetch build context').argument('<build-id>', 'Build ID to fetch context for').option('--source <source>', 'Context source: auto, cloud, or local', 'auto').option('--agent', 'Output compact context for LLM agents').option('--full', 'Return the full build context payload with --agent --json').option('--include <items>', 'Add detail to compact agent JSON: screenshots,diffs,comments').addHelpText('after', `
|
|
517
517
|
Examples:
|
|
518
518
|
$ vizzly context build abc123
|
|
519
519
|
$ vizzly context build current --source local
|
|
@@ -890,7 +890,7 @@ program.command('whoami').description('Show current authentication status and us
|
|
|
890
890
|
});
|
|
891
891
|
|
|
892
892
|
// Save user's PATH for menubar app (non-blocking, runs in background)
|
|
893
|
-
// This auto-configures the menubar app so it can find
|
|
893
|
+
// This auto-configures the menubar app so it can find package runners/node
|
|
894
894
|
saveUserPath().catch(() => {});
|
|
895
895
|
let commandNames = new Set(program.commands.map(command => command.name()));
|
|
896
896
|
let normalizedArgv = normalizeJsonArgv(process.argv, commandNames);
|
package/dist/client/index.js
CHANGED
|
@@ -192,6 +192,7 @@ function createSimpleClient(serverUrl) {
|
|
|
192
192
|
let isFilePath = typeof imageBuffer === 'string';
|
|
193
193
|
let image = isFilePath ? imageBuffer : imageBuffer.toString('base64');
|
|
194
194
|
let type = isFilePath ? 'file-path' : 'base64';
|
|
195
|
+
let requestTimeout = options.requestTimeout || DEFAULT_TIMEOUT_MS;
|
|
195
196
|
let properties = createScreenshotProperties(options);
|
|
196
197
|
let httpStart = Date.now();
|
|
197
198
|
let {
|
|
@@ -203,7 +204,7 @@ function createSimpleClient(serverUrl) {
|
|
|
203
204
|
image,
|
|
204
205
|
type,
|
|
205
206
|
properties
|
|
206
|
-
},
|
|
207
|
+
}, requestTimeout);
|
|
207
208
|
let httpMs = Date.now() - httpStart;
|
|
208
209
|
if (shouldLogClient('debug')) {
|
|
209
210
|
console.debug(`[vizzly-client] ${name} HTTP completed in ${httpMs}ms`);
|
package/dist/commands/doctor.js
CHANGED
|
@@ -234,11 +234,11 @@ export async function doctorCommand(options = {}, globalOptions = {}, deps = {})
|
|
|
234
234
|
output.printErr(` ${colors.dim('─'.repeat(52))}`);
|
|
235
235
|
for (let item of contextItems) {
|
|
236
236
|
if (item.type === 'success') {
|
|
237
|
-
output.printErr(` ${colors.
|
|
237
|
+
output.printErr(` ${colors.success('✓')} ${colors.brand.textMuted(item.label)} ${colors.white(item.value)}`);
|
|
238
238
|
} else if (item.type === 'warning') {
|
|
239
|
-
output.printErr(` ${colors.yellow('!')} ${colors.
|
|
239
|
+
output.printErr(` ${colors.yellow('!')} ${colors.brand.textMuted(item.label)} ${colors.yellow(item.value)}`);
|
|
240
240
|
} else {
|
|
241
|
-
output.printErr(` ${colors.dim('○')} ${colors.
|
|
241
|
+
output.printErr(` ${colors.dim('○')} ${colors.brand.textMuted(item.label)} ${colors.dim(item.value)}`);
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
}
|
|
@@ -246,7 +246,7 @@ export async function doctorCommand(options = {}, globalOptions = {}, deps = {})
|
|
|
246
246
|
// Footer with links
|
|
247
247
|
output.printErr('');
|
|
248
248
|
output.printErr(` ${colors.dim('─'.repeat(52))}`);
|
|
249
|
-
output.printErr(` ${colors.dim('Docs')} ${colors.
|
|
249
|
+
output.printErr(` ${colors.dim('Docs')} ${colors.info(colors.underline('docs.vizzly.dev'))} ${colors.dim('GitHub')} ${colors.info(colors.underline('github.com/vizzly-testing/cli'))}`);
|
|
250
250
|
|
|
251
251
|
// Emit structured data in verbose mode (in addition to visual output)
|
|
252
252
|
if (globalOptions.verbose) {
|
package/dist/commands/init.js
CHANGED
|
@@ -160,7 +160,7 @@ function writeHumanCreatedOutput(output, plugins) {
|
|
|
160
160
|
function showNextSteps(output) {
|
|
161
161
|
output.blank();
|
|
162
162
|
output.labelValue('Next steps', '');
|
|
163
|
-
output.list(['Set your API token: export VIZZLY_TOKEN="your-api-key"', 'Run your tests with Vizzly:
|
|
163
|
+
output.list(['Set your API token: export VIZZLY_TOKEN="your-api-key"', 'Run your tests with Vizzly: pnpm exec vizzly run "pnpm test"', 'Upload screenshots: pnpm exec vizzly upload ./screenshots']);
|
|
164
164
|
}
|
|
165
165
|
async function writeConfigFile({
|
|
166
166
|
configPath,
|
package/dist/commands/preview.js
CHANGED
|
@@ -335,7 +335,7 @@ export async function previewCommand(path, options = {}, globalOptions = {}, dep
|
|
|
335
335
|
output.blank();
|
|
336
336
|
output.print(' Run visual tests first, then upload your preview:');
|
|
337
337
|
output.blank();
|
|
338
|
-
output.print(' vizzly run "
|
|
338
|
+
output.print(' vizzly run "pnpm test"');
|
|
339
339
|
output.print(' vizzly preview ./dist');
|
|
340
340
|
output.blank();
|
|
341
341
|
output.print(' Or specify a build explicitly:');
|
|
@@ -591,13 +591,13 @@ export async function previewCommand(path, options = {}, globalOptions = {}, dep
|
|
|
591
591
|
output.print(` ${colors.brand.textTertiary('Files')} ${colors.white(result.uploaded)} (${formatBytes(result.totalBytes)} compressed)`);
|
|
592
592
|
if (result.reusedBlobs > 0) {
|
|
593
593
|
let savedBytes = result.totalBytes - result.newBytes;
|
|
594
|
-
output.print(` ${colors.brand.textTertiary('Deduped')} ${colors.
|
|
594
|
+
output.print(` ${colors.brand.textTertiary('Deduped')} ${colors.success(result.reusedBlobs)} files (saved ${formatBytes(savedBytes)})`);
|
|
595
595
|
}
|
|
596
596
|
if (result.basePath) {
|
|
597
597
|
output.print(` ${colors.brand.textTertiary('Base path')} ${colors.dim(result.basePath)}`);
|
|
598
598
|
}
|
|
599
599
|
output.blank();
|
|
600
|
-
output.print(` ${colors.brand.textTertiary('Preview')} ${colors.
|
|
600
|
+
output.print(` ${colors.brand.textTertiary('Preview')} ${colors.info(colors.underline(result.previewUrl))}`);
|
|
601
601
|
|
|
602
602
|
// Open in browser if requested
|
|
603
603
|
if (options.open) {
|
package/dist/commands/run.js
CHANGED
|
@@ -348,7 +348,7 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
|
|
|
348
348
|
getTokenContext
|
|
349
349
|
});
|
|
350
350
|
if (displayUrl) {
|
|
351
|
-
output.print(` ${colors.brand.textTertiary('Results')} ${colors.
|
|
351
|
+
output.print(` ${colors.brand.textTertiary('Results')} ${colors.info(colors.underline(displayUrl))}`);
|
|
352
352
|
} else {
|
|
353
353
|
output.print(` ${colors.brand.textTertiary('Build')} ${colors.dim(result.buildId)}`);
|
|
354
354
|
}
|
|
@@ -435,7 +435,7 @@ export async function tddStartCommand(options = {}, globalOptions = {}) {
|
|
|
435
435
|
if (globalOptions.verbose) {
|
|
436
436
|
output.blank();
|
|
437
437
|
output.print(` ${colors.brand.textTertiary('Next steps')}`);
|
|
438
|
-
output.print(` ${colors.brand.textMuted('1.')} Run tests in watch mode ${colors.brand.textMuted('(
|
|
438
|
+
output.print(` ${colors.brand.textMuted('1.')} Run tests in watch mode ${colors.brand.textMuted('(pnpm test -- --watch)')}`);
|
|
439
439
|
output.print(` ${colors.brand.textMuted('2.')} Review visual changes in the dashboard`);
|
|
440
440
|
output.print(` ${colors.brand.textMuted('3.')} Accept or reject baseline updates`);
|
|
441
441
|
}
|