@vizzly-testing/cli 0.3.2 → 0.5.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 (60) hide show
  1. package/README.md +50 -28
  2. package/dist/cli.js +34 -30
  3. package/dist/client/index.js +1 -1
  4. package/dist/commands/run.js +38 -11
  5. package/dist/commands/tdd.js +30 -18
  6. package/dist/commands/upload.js +56 -5
  7. package/dist/server/handlers/api-handler.js +83 -0
  8. package/dist/server/handlers/tdd-handler.js +138 -0
  9. package/dist/server/http-server.js +132 -0
  10. package/dist/services/api-service.js +22 -2
  11. package/dist/services/server-manager.js +45 -29
  12. package/dist/services/test-runner.js +66 -69
  13. package/dist/services/uploader.js +11 -4
  14. package/dist/types/commands/run.d.ts +4 -1
  15. package/dist/types/commands/tdd.d.ts +5 -1
  16. package/dist/types/sdk/index.d.ts +6 -6
  17. package/dist/types/server/handlers/api-handler.d.ts +49 -0
  18. package/dist/types/server/handlers/tdd-handler.d.ts +85 -0
  19. package/dist/types/server/http-server.d.ts +5 -0
  20. package/dist/types/services/api-service.d.ts +1 -0
  21. package/dist/types/services/server-manager.d.ts +148 -3
  22. package/dist/types/services/test-runner.d.ts +1 -0
  23. package/dist/types/services/uploader.d.ts +2 -1
  24. package/dist/types/utils/ci-env.d.ts +55 -0
  25. package/dist/types/utils/config-helpers.d.ts +1 -1
  26. package/dist/types/utils/console-ui.d.ts +1 -1
  27. package/dist/types/utils/git.d.ts +12 -0
  28. package/dist/utils/ci-env.js +293 -0
  29. package/dist/utils/console-ui.js +4 -14
  30. package/dist/utils/git.js +38 -0
  31. package/docs/api-reference.md +17 -5
  32. package/docs/getting-started.md +1 -1
  33. package/docs/tdd-mode.md +9 -9
  34. package/docs/test-integration.md +9 -17
  35. package/docs/upload-command.md +7 -0
  36. package/package.json +4 -5
  37. package/dist/screenshot-wrapper.js +0 -68
  38. package/dist/server/index.js +0 -522
  39. package/dist/services/service-utils.js +0 -171
  40. package/dist/types/index.js +0 -153
  41. package/dist/types/screenshot-wrapper.d.ts +0 -27
  42. package/dist/types/server/index.d.ts +0 -38
  43. package/dist/types/services/service-utils.d.ts +0 -45
  44. package/dist/types/types/index.d.ts +0 -373
  45. package/dist/types/utils/diagnostics.d.ts +0 -69
  46. package/dist/types/utils/error-messages.d.ts +0 -42
  47. package/dist/types/utils/framework-detector.d.ts +0 -5
  48. package/dist/types/utils/help.d.ts +0 -11
  49. package/dist/types/utils/image-comparison.d.ts +0 -42
  50. package/dist/types/utils/package.d.ts +0 -1
  51. package/dist/types/utils/project-detection.d.ts +0 -19
  52. package/dist/types/utils/ui-helpers.d.ts +0 -23
  53. package/dist/utils/diagnostics.js +0 -184
  54. package/dist/utils/error-messages.js +0 -34
  55. package/dist/utils/framework-detector.js +0 -40
  56. package/dist/utils/help.js +0 -66
  57. package/dist/utils/image-comparison.js +0 -172
  58. package/dist/utils/package.js +0 -9
  59. package/dist/utils/project-detection.js +0 -145
  60. package/dist/utils/ui-helpers.js +0 -86
@@ -0,0 +1,293 @@
1
+ /**
2
+ * CI Environment Detection
3
+ *
4
+ * Generic functions to extract git and PR information from any CI provider
5
+ */
6
+
7
+ /**
8
+ * Get the branch name from CI environment variables
9
+ * @returns {string|null} Branch name or null if not available
10
+ */
11
+ export function getBranch() {
12
+ return process.env.VIZZLY_BRANCH ||
13
+ // Vizzly override
14
+ process.env.GITHUB_HEAD_REF ||
15
+ // GitHub Actions (for PRs)
16
+ process.env.GITHUB_REF_NAME ||
17
+ // GitHub Actions (for pushes)
18
+ process.env.CI_COMMIT_REF_NAME ||
19
+ // GitLab CI
20
+ process.env.CIRCLE_BRANCH ||
21
+ // CircleCI
22
+ process.env.TRAVIS_BRANCH ||
23
+ // Travis CI
24
+ process.env.BUILDKITE_BRANCH ||
25
+ // Buildkite
26
+ process.env.DRONE_BRANCH ||
27
+ // Drone CI
28
+ process.env.BRANCH_NAME ||
29
+ // Jenkins
30
+ process.env.GIT_BRANCH ||
31
+ // Jenkins (alternative)
32
+ process.env.BITBUCKET_BRANCH ||
33
+ // Bitbucket Pipelines
34
+ process.env.WERCKER_GIT_BRANCH ||
35
+ // Wercker
36
+ process.env.APPVEYOR_REPO_BRANCH ||
37
+ // AppVeyor
38
+ process.env.BUILD_SOURCEBRANCH?.replace(/^refs\/heads\//, '') ||
39
+ // Azure DevOps
40
+ process.env.CODEBUILD_WEBHOOK_HEAD_REF?.replace(/^refs\/heads\//, '') ||
41
+ // AWS CodeBuild
42
+ process.env.SEMAPHORE_GIT_BRANCH ||
43
+ // Semaphore
44
+ null;
45
+ }
46
+
47
+ /**
48
+ * Get the commit SHA from CI environment variables
49
+ * @returns {string|null} Commit SHA or null if not available
50
+ */
51
+ export function getCommit() {
52
+ return process.env.VIZZLY_COMMIT_SHA ||
53
+ // Vizzly override
54
+ process.env.GITHUB_SHA ||
55
+ // GitHub Actions
56
+ process.env.CI_COMMIT_SHA ||
57
+ // GitLab CI
58
+ process.env.CIRCLE_SHA1 ||
59
+ // CircleCI
60
+ process.env.TRAVIS_COMMIT ||
61
+ // Travis CI
62
+ process.env.BUILDKITE_COMMIT ||
63
+ // Buildkite
64
+ process.env.DRONE_COMMIT_SHA ||
65
+ // Drone CI
66
+ process.env.GIT_COMMIT ||
67
+ // Jenkins
68
+ process.env.BITBUCKET_COMMIT ||
69
+ // Bitbucket Pipelines
70
+ process.env.WERCKER_GIT_COMMIT ||
71
+ // Wercker
72
+ process.env.APPVEYOR_REPO_COMMIT ||
73
+ // AppVeyor
74
+ process.env.BUILD_SOURCEVERSION ||
75
+ // Azure DevOps
76
+ process.env.CODEBUILD_RESOLVED_SOURCE_VERSION ||
77
+ // AWS CodeBuild
78
+ process.env.SEMAPHORE_GIT_SHA ||
79
+ // Semaphore
80
+ process.env.HEROKU_TEST_RUN_COMMIT_VERSION ||
81
+ // Heroku CI
82
+ process.env.COMMIT_SHA ||
83
+ // Generic
84
+ process.env.HEAD_COMMIT ||
85
+ // Alternative generic
86
+ process.env.SHA ||
87
+ // Another generic option
88
+ null;
89
+ }
90
+
91
+ /**
92
+ * Get the commit message from CI environment variables
93
+ * @returns {string|null} Commit message or null if not available
94
+ */
95
+ export function getCommitMessage() {
96
+ return process.env.VIZZLY_COMMIT_MESSAGE ||
97
+ // Vizzly override
98
+ process.env.CI_COMMIT_MESSAGE ||
99
+ // GitLab CI
100
+ process.env.TRAVIS_COMMIT_MESSAGE ||
101
+ // Travis CI
102
+ process.env.BUILDKITE_MESSAGE ||
103
+ // Buildkite
104
+ process.env.DRONE_COMMIT_MESSAGE ||
105
+ // Drone CI
106
+ process.env.APPVEYOR_REPO_COMMIT_MESSAGE ||
107
+ // AppVeyor
108
+ process.env.COMMIT_MESSAGE ||
109
+ // Generic
110
+ null;
111
+ }
112
+
113
+ /**
114
+ * Get the pull request number from CI environment variables
115
+ * @returns {number|null} PR number or null if not available/not a PR
116
+ */
117
+ export function getPullRequestNumber() {
118
+ // Check VIZZLY override first
119
+ if (process.env.VIZZLY_PR_NUMBER) {
120
+ return parseInt(process.env.VIZZLY_PR_NUMBER, 10);
121
+ }
122
+
123
+ // GitHub Actions - extract from GITHUB_REF
124
+ if (process.env.GITHUB_ACTIONS && process.env.GITHUB_EVENT_NAME === 'pull_request') {
125
+ const prMatch = process.env.GITHUB_REF?.match(/refs\/pull\/(\d+)\/merge/);
126
+ if (prMatch?.[1]) return parseInt(prMatch[1], 10);
127
+ }
128
+
129
+ // GitLab CI
130
+ if (process.env.CI_MERGE_REQUEST_ID) {
131
+ return parseInt(process.env.CI_MERGE_REQUEST_ID, 10);
132
+ }
133
+
134
+ // CircleCI - extract from PR URL
135
+ if (process.env.CIRCLE_PULL_REQUEST) {
136
+ const prMatch = process.env.CIRCLE_PULL_REQUEST.match(/\/pull\/(\d+)$/);
137
+ if (prMatch?.[1]) return parseInt(prMatch[1], 10);
138
+ }
139
+
140
+ // Travis CI
141
+ if (process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false') {
142
+ return parseInt(process.env.TRAVIS_PULL_REQUEST, 10);
143
+ }
144
+
145
+ // Buildkite
146
+ if (process.env.BUILDKITE_PULL_REQUEST && process.env.BUILDKITE_PULL_REQUEST !== 'false') {
147
+ return parseInt(process.env.BUILDKITE_PULL_REQUEST, 10);
148
+ }
149
+
150
+ // Drone CI
151
+ if (process.env.DRONE_PULL_REQUEST) {
152
+ return parseInt(process.env.DRONE_PULL_REQUEST, 10);
153
+ }
154
+
155
+ // Jenkins (GitHub Pull Request Builder plugin)
156
+ if (process.env.ghprbPullId) {
157
+ return parseInt(process.env.ghprbPullId, 10);
158
+ }
159
+
160
+ // Azure DevOps
161
+ if (process.env.SYSTEM_PULLREQUEST_PULLREQUESTID) {
162
+ return parseInt(process.env.SYSTEM_PULLREQUEST_PULLREQUESTID, 10);
163
+ }
164
+
165
+ // AppVeyor
166
+ if (process.env.APPVEYOR_PULL_REQUEST_NUMBER) {
167
+ return parseInt(process.env.APPVEYOR_PULL_REQUEST_NUMBER, 10);
168
+ }
169
+ return null;
170
+ }
171
+
172
+ /**
173
+ * Get the PR head SHA from CI environment variables
174
+ * @returns {string|null} PR head SHA or null if not available
175
+ */
176
+ export function getPullRequestHeadSha() {
177
+ return process.env.VIZZLY_PR_HEAD_SHA ||
178
+ // Vizzly override
179
+ process.env.GITHUB_SHA ||
180
+ // GitHub Actions
181
+ process.env.CI_COMMIT_SHA ||
182
+ // GitLab CI
183
+ process.env.CIRCLE_SHA1 ||
184
+ // CircleCI
185
+ process.env.TRAVIS_COMMIT ||
186
+ // Travis CI
187
+ process.env.BUILDKITE_COMMIT ||
188
+ // Buildkite
189
+ process.env.DRONE_COMMIT_SHA ||
190
+ // Drone CI
191
+ process.env.ghprbActualCommit ||
192
+ // Jenkins
193
+ process.env.GIT_COMMIT ||
194
+ // Jenkins fallback
195
+ process.env.BUILD_SOURCEVERSION ||
196
+ // Azure DevOps
197
+ process.env.APPVEYOR_REPO_COMMIT ||
198
+ // AppVeyor
199
+ null;
200
+ }
201
+
202
+ /**
203
+ * Get the PR base SHA from CI environment variables
204
+ * @returns {string|null} PR base SHA or null if not available
205
+ */
206
+ export function getPullRequestBaseSha() {
207
+ return process.env.VIZZLY_PR_BASE_SHA ||
208
+ // Vizzly override
209
+ process.env.CI_MERGE_REQUEST_TARGET_BRANCH_SHA ||
210
+ // GitLab CI
211
+ null // Most CIs don't provide this
212
+ ;
213
+ }
214
+
215
+ /**
216
+ * Get the PR head ref (branch) from CI environment variables
217
+ * @returns {string|null} PR head ref or null if not available
218
+ */
219
+ export function getPullRequestHeadRef() {
220
+ return process.env.VIZZLY_PR_HEAD_REF ||
221
+ // Vizzly override
222
+ process.env.GITHUB_HEAD_REF ||
223
+ // GitHub Actions
224
+ process.env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME ||
225
+ // GitLab CI
226
+ process.env.TRAVIS_PULL_REQUEST_BRANCH ||
227
+ // Travis CI
228
+ process.env.DRONE_SOURCE_BRANCH ||
229
+ // Drone CI
230
+ process.env.ghprbSourceBranch ||
231
+ // Jenkins
232
+ process.env.SYSTEM_PULLREQUEST_SOURCEBRANCH?.replace(/^refs\/heads\//, '') ||
233
+ // Azure DevOps
234
+ process.env.APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH ||
235
+ // AppVeyor
236
+ null;
237
+ }
238
+
239
+ /**
240
+ * Get the PR base ref (target branch) from CI environment variables
241
+ * @returns {string|null} PR base ref or null if not available
242
+ */
243
+ export function getPullRequestBaseRef() {
244
+ return process.env.VIZZLY_PR_BASE_REF ||
245
+ // Vizzly override
246
+ process.env.GITHUB_BASE_REF ||
247
+ // GitHub Actions
248
+ process.env.CI_MERGE_REQUEST_TARGET_BRANCH_NAME ||
249
+ // GitLab CI
250
+ process.env.TRAVIS_BRANCH ||
251
+ // Travis CI (target branch)
252
+ process.env.BUILDKITE_PULL_REQUEST_BASE_BRANCH ||
253
+ // Buildkite
254
+ process.env.DRONE_TARGET_BRANCH ||
255
+ // Drone CI
256
+ process.env.ghprbTargetBranch ||
257
+ // Jenkins
258
+ process.env.SYSTEM_PULLREQUEST_TARGETBRANCH?.replace(/^refs\/heads\//, '') ||
259
+ // Azure DevOps
260
+ process.env.APPVEYOR_REPO_BRANCH ||
261
+ // AppVeyor (target branch)
262
+ null;
263
+ }
264
+
265
+ /**
266
+ * Check if we're currently in a pull request context
267
+ * @returns {boolean} True if in a PR context
268
+ */
269
+ export function isPullRequest() {
270
+ return getPullRequestNumber() !== null;
271
+ }
272
+
273
+ /**
274
+ * Get the CI provider name
275
+ * @returns {string} CI provider name or 'unknown'
276
+ */
277
+ export function getCIProvider() {
278
+ if (process.env.GITHUB_ACTIONS) return 'github-actions';
279
+ if (process.env.GITLAB_CI) return 'gitlab-ci';
280
+ if (process.env.CIRCLECI) return 'circleci';
281
+ if (process.env.TRAVIS) return 'travis-ci';
282
+ if (process.env.BUILDKITE) return 'buildkite';
283
+ if (process.env.DRONE) return 'drone-ci';
284
+ if (process.env.JENKINS_URL) return 'jenkins';
285
+ if (process.env.AZURE_HTTP_USER_AGENT || process.env.TF_BUILD) return 'azure-devops';
286
+ if (process.env.CODEBUILD_BUILD_ID) return 'aws-codebuild';
287
+ if (process.env.APPVEYOR) return 'appveyor';
288
+ if (process.env.SEMAPHORE) return 'semaphore';
289
+ if (process.env.WERCKER) return 'wercker';
290
+ if (process.env.BITBUCKET_BUILD_NUMBER) return 'bitbucket-pipelines';
291
+ if (process.env.HEROKU_TEST_RUN_ID) return 'heroku-ci';
292
+ return 'unknown';
293
+ }
@@ -158,11 +158,12 @@ export class ConsoleUI {
158
158
  startSpinner(message) {
159
159
  if (this.json || !process.stdout.isTTY) return;
160
160
  this.stopSpinner();
161
+ this.currentMessage = message;
161
162
  const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
162
163
  let i = 0;
163
164
  this.spinner = setInterval(() => {
164
165
  const frame = frames[i++ % frames.length];
165
- const line = `${this.colors.blue(frame)} ${message}`;
166
+ const line = `${this.colors.blue(frame)} ${this.currentMessage || message}`;
166
167
 
167
168
  // Clear previous line and write new one
168
169
  process.stdout.write('\r' + ' '.repeat(this.lastLine.length) + '\r');
@@ -211,16 +212,5 @@ export class ConsoleUI {
211
212
  }
212
213
  }
213
214
 
214
- // Ensure spinner is cleaned up on process exit
215
- process.on('exit', () => {
216
- // Clear any remaining spinner
217
- if (process.stdout.isTTY) {
218
- process.stdout.write('\r' + ' '.repeat(80) + '\r');
219
- }
220
- });
221
- process.on('SIGINT', () => {
222
- if (process.stdout.isTTY) {
223
- process.stdout.write('\r' + ' '.repeat(80) + '\r');
224
- }
225
- process.exit(1);
226
- });
215
+ // Note: Global process event listeners are handled in individual commands
216
+ // to avoid interference between tests and proper cleanup
package/dist/utils/git.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { exec } from 'child_process';
2
2
  import { promisify } from 'util';
3
+ import { getBranch as getCIBranch, getCommit as getCICommit, getCommitMessage as getCICommitMessage, getPullRequestNumber } from './ci-env.js';
3
4
  const execAsync = promisify(exec);
4
5
  export async function getCommonAncestor(commit1, commit2, cwd = process.cwd()) {
5
6
  try {
@@ -144,6 +145,23 @@ export async function getCommitMessage(cwd = process.cwd()) {
144
145
  }
145
146
  }
146
147
 
148
+ /**
149
+ * Detect commit message with override and environment variable support
150
+ * @param {string} override - Commit message override from CLI
151
+ * @param {string} cwd - Working directory
152
+ * @returns {Promise<string|null>} Commit message or null if not available
153
+ */
154
+ export async function detectCommitMessage(override = null, cwd = process.cwd()) {
155
+ if (override) return override;
156
+
157
+ // Try CI environment variables first
158
+ const ciCommitMessage = getCICommitMessage();
159
+ if (ciCommitMessage) return ciCommitMessage;
160
+
161
+ // Fallback to regular git log
162
+ return await getCommitMessage(cwd);
163
+ }
164
+
147
165
  /**
148
166
  * Check if the working directory is a git repository
149
167
  * @param {string} cwd - Working directory
@@ -193,6 +211,12 @@ export async function getGitStatus(cwd = process.cwd()) {
193
211
  */
194
212
  export async function detectBranch(override = null, cwd = process.cwd()) {
195
213
  if (override) return override;
214
+
215
+ // Try CI environment variables first
216
+ const ciBranch = getCIBranch();
217
+ if (ciBranch) return ciBranch;
218
+
219
+ // Fallback to git command when no CI environment variables
196
220
  const currentBranch = await getCurrentBranch(cwd);
197
221
  return currentBranch || 'unknown';
198
222
  }
@@ -205,6 +229,12 @@ export async function detectBranch(override = null, cwd = process.cwd()) {
205
229
  */
206
230
  export async function detectCommit(override = null, cwd = process.cwd()) {
207
231
  if (override) return override;
232
+
233
+ // Try CI environment variables first
234
+ const ciCommit = getCICommit();
235
+ if (ciCommit) return ciCommit;
236
+
237
+ // Fallback to git command when no CI environment variables
208
238
  return await getCurrentCommitSha(cwd);
209
239
  }
210
240
 
@@ -223,4 +253,12 @@ export async function generateBuildNameWithGit(override = null, cwd = process.cw
223
253
  return `${branch}-${shortCommit}`;
224
254
  }
225
255
  return generateBuildName();
256
+ }
257
+
258
+ /**
259
+ * Detect pull request number from CI environment
260
+ * @returns {number|null} Pull request number or null if not in PR context
261
+ */
262
+ export function detectPullRequestNumber() {
263
+ return getPullRequestNumber();
226
264
  }
@@ -288,6 +288,7 @@ Upload screenshots from a directory.
288
288
  - `--threshold <number>` - Comparison threshold (0-1)
289
289
  - `--token <token>` - API token override
290
290
  - `--wait` - Wait for build completion
291
+ - `--upload-all` - Upload all screenshots without SHA deduplication
291
292
 
292
293
  **Exit Codes:**
293
294
  - `0` - Success (all approved or no changes)
@@ -316,18 +317,14 @@ Run tests with Vizzly integration.
316
317
 
317
318
  *Processing Options:*
318
319
  - `--wait` - Wait for build completion and exit with appropriate code
319
- - `--eager` - Create build immediately (default: lazy creation)
320
320
  - `--threshold <number>` - Comparison threshold (0-1, default: 0.01)
321
321
  - `--upload-timeout <ms>` - Upload wait timeout in ms (default: from config or 30000)
322
+ - `--upload-all` - Upload all screenshots without SHA deduplication
322
323
 
323
324
  *Development & Testing:*
324
- - `--tdd` - Enable TDD mode with local comparisons
325
325
  - `--allow-no-token` - Allow running without API token
326
326
  - `--token <token>` - API token override
327
327
 
328
- *Baseline Configuration:*
329
- - `--baseline-build <id>` - Use specific build as baseline for comparisons
330
- - `--baseline-comparison <id>` - Use specific comparison as baseline
331
328
 
332
329
  **Environment Variables Set:**
333
330
  - `VIZZLY_SERVER_URL` - Local server URL
@@ -484,14 +481,29 @@ Configuration loaded via cosmiconfig in this order:
484
481
 
485
482
  ### Environment Variables
486
483
 
484
+ **Core Configuration:**
487
485
  - `VIZZLY_TOKEN` - API authentication token
488
486
  - `VIZZLY_API_URL` - API base URL override
489
487
  - `VIZZLY_LOG_LEVEL` - Logger level (`debug`, `info`, `warn`, `error`)
488
+
489
+ **Git Information Override (CI/CD Enhancement):**
490
+ - `VIZZLY_COMMIT_SHA` - Override detected commit SHA
491
+ - `VIZZLY_COMMIT_MESSAGE` - Override detected commit message
492
+ - `VIZZLY_BRANCH` - Override detected branch name
493
+ - `VIZZLY_PR_NUMBER` - Override detected pull request number
494
+
495
+ **Runtime (Set by CLI):**
490
496
  - `VIZZLY_SERVER_URL` - Screenshot server URL (set by CLI)
491
497
  - `VIZZLY_ENABLED` - Enable/disable client (set by CLI)
492
498
  - `VIZZLY_BUILD_ID` - Current build ID (set by CLI)
493
499
  - `VIZZLY_TDD_MODE` - TDD mode active (set by CLI)
494
500
 
501
+ **Priority Order for Git Information:**
502
+ 1. CLI arguments (`--commit`, `--branch`, `--message`)
503
+ 2. `VIZZLY_*` environment variables
504
+ 3. CI-specific environment variables (e.g., `GITHUB_SHA`, `CI_COMMIT_SHA`)
505
+ 4. Git command detection
506
+
495
507
  ## Error Handling
496
508
 
497
509
  ### Client Errors
@@ -59,7 +59,7 @@ npx vizzly upload ./screenshots --build-name "Release v1.2.3"
59
59
  npx vizzly run "npm test"
60
60
 
61
61
  # Use TDD mode for local development
62
- npx vizzly run "npm test" --tdd
62
+ npx vizzly tdd "npm test"
63
63
  ```
64
64
 
65
65
  ### 6. In your test code
package/docs/tdd-mode.md CHANGED
@@ -145,11 +145,11 @@ vizzly tdd "npm test" --timeout 60000
145
145
  npx vizzly run "npm test" --wait
146
146
 
147
147
  # 2. Start TDD development
148
- npx vizzly run "npm test" --tdd
148
+ npx vizzly tdd "npm test"
149
149
 
150
150
  # 3. Make changes and iterate
151
151
  # Edit code...
152
- npx vizzly run "npm test" --tdd
152
+ npx vizzly tdd "npm test"
153
153
 
154
154
  # 4. Upload when satisfied
155
155
  npx vizzly run "npm test" --wait
@@ -159,13 +159,13 @@ npx vizzly run "npm test" --wait
159
159
 
160
160
  ```bash
161
161
  # Start with latest baselines
162
- npx vizzly run "npm test" --tdd
162
+ npx vizzly tdd "npm test"
163
163
 
164
164
  # Develop new feature with immediate feedback
165
165
  while [ $? -ne 0 ]; do
166
166
  # Edit code to fix visual differences
167
167
  vim src/components/NewFeature.js
168
- npx vizzly run "npm test" --tdd
168
+ npx vizzly tdd "npm test"
169
169
  done
170
170
 
171
171
  # Upload completed feature
@@ -176,7 +176,7 @@ npx vizzly run "npm test" --build-name "Feature: New Dashboard"
176
176
 
177
177
  ```bash
178
178
  # Use TDD mode to verify fixes
179
- npx vizzly run "npm test" --tdd
179
+ npx vizzly tdd "npm test"
180
180
 
181
181
  # Tests should pass when bug is fixed
182
182
  # Then upload the fix
@@ -216,7 +216,7 @@ npx vizzly status # Shows latest build info
216
216
  Download new baselines from a different build:
217
217
 
218
218
  ```bash
219
- npx vizzly run "npm test" --tdd --baseline-build build-xyz789
219
+ npx vizzly tdd "npm test" --baseline-build build-xyz789
220
220
  ```
221
221
 
222
222
  ### Force Baseline Refresh
@@ -225,7 +225,7 @@ Delete local baselines to force re-download:
225
225
 
226
226
  ```bash
227
227
  rm -rf .vizzly/baselines/
228
- npx vizzly run "npm test" --tdd
228
+ npx vizzly tdd "npm test"
229
229
  ```
230
230
 
231
231
  ## Advanced Usage
@@ -276,7 +276,7 @@ jobs:
276
276
  # Use TDD mode for PR builds (faster, no uploads)
277
277
  - name: TDD Visual Tests (PR)
278
278
  if: github.event_name == 'pull_request'
279
- run: npx vizzly run "npm test" --tdd
279
+ run: npx vizzly tdd "npm test"
280
280
  env:
281
281
  VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
282
282
 
@@ -337,7 +337,7 @@ Error: Failed to compare 'homepage': baseline image not found
337
337
  **Solution**: Refresh baselines:
338
338
  ```bash
339
339
  rm -rf .vizzly/baselines/
340
- npx vizzly run "npm test" --tdd
340
+ npx vizzly tdd "npm test"
341
341
  ```
342
342
 
343
343
  ### Odiff Not Found
@@ -199,9 +199,9 @@ vizzly run "npm test" --branch "feature/new-ui"
199
199
  vizzly run "npm test" --wait
200
200
  ```
201
201
 
202
- **`--eager`** - Create build immediately (default: lazy)
202
+ **`--upload-all`** - Upload all screenshots without SHA deduplication
203
203
  ```bash
204
- vizzly run "npm test" --eager
204
+ vizzly run "npm test" --upload-all
205
205
  ```
206
206
 
207
207
  **`--threshold <number>`** - Comparison threshold (0-1, default: 0.01)
@@ -211,10 +211,7 @@ vizzly run "npm test" --threshold 0.02
211
211
 
212
212
  ### Development Options
213
213
 
214
- **`--tdd`** - Enable TDD mode (see [TDD Mode Guide](./tdd-mode.md))
215
- ```bash
216
- vizzly run "npm test" --tdd
217
- ```
214
+ For TDD mode, use the dedicated `vizzly tdd` command. See [TDD Mode Guide](./tdd-mode.md) for details.
218
215
 
219
216
  **`--allow-no-token`** - Allow running without API token
220
217
  ```bash
@@ -226,17 +223,6 @@ vizzly run "npm test" --allow-no-token
226
223
  vizzly run "npm test" --token "your-token-here"
227
224
  ```
228
225
 
229
- ### Baseline Configuration
230
-
231
- **`--baseline-build <id>`** - Use specific build as baseline
232
- ```bash
233
- vizzly run "npm test" --baseline-build "build_123"
234
- ```
235
-
236
- **`--baseline-comparison <id>`** - Use specific comparison as baseline
237
- ```bash
238
- vizzly run "npm test" --baseline-comparison "comp_456"
239
- ```
240
226
 
241
227
  ## Screenshot Properties
242
228
 
@@ -289,8 +275,14 @@ jobs:
289
275
  - run: npx vizzly run "npm test" --wait
290
276
  env:
291
277
  VIZZLY_TOKEN: ${{ secrets.VIZZLY_TOKEN }}
278
+ # Optional: Enhanced git information from GitHub context
279
+ VIZZLY_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
280
+ VIZZLY_COMMIT_SHA: ${{ github.event.head_commit.id }}
281
+ VIZZLY_BRANCH: ${{ github.head_ref || github.ref_name }}
292
282
  ```
293
283
 
284
+ **Enhanced Git Information:** The `VIZZLY_*` environment variables ensure accurate git metadata is captured in your builds, avoiding issues with merge commits that can occur in CI environments.
285
+
294
286
  ### GitLab CI
295
287
 
296
288
  ```yaml
@@ -72,6 +72,13 @@ This will:
72
72
  - Show progress and results
73
73
  - Exit with appropriate status code
74
74
 
75
+ **`--upload-all`** - Upload all screenshots without SHA deduplication
76
+ ```bash
77
+ vizzly upload ./screenshots --upload-all
78
+ ```
79
+
80
+ By default, Vizzly deduplicates screenshots based on their SHA hash to avoid unnecessary uploads. Use this flag to force upload of all screenshots regardless of their content.
81
+
75
82
  **`--metadata <json>`** - Additional metadata as JSON
76
83
  ```bash
77
84
  vizzly upload ./screenshots --metadata '{"browser": "chrome", "viewport": "1920x1080"}'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizzly-testing/cli",
3
- "version": "0.3.2",
3
+ "version": "0.5.0",
4
4
  "description": "Visual review platform for UI developers and designers",
5
5
  "keywords": [
6
6
  "visual-testing",
@@ -73,11 +73,11 @@
73
73
  "registry": "https://registry.npmjs.org/"
74
74
  },
75
75
  "dependencies": {
76
- "commander": "^11.1.0",
76
+ "commander": "^14.0.0",
77
77
  "cosmiconfig": "^9.0.0",
78
78
  "dotenv": "^17.2.1",
79
79
  "form-data": "^4.0.0",
80
- "glob": "^10.3.10",
80
+ "glob": "^11.0.3",
81
81
  "odiff-bin": "^3.2.1"
82
82
  },
83
83
  "devDependencies": {
@@ -93,8 +93,7 @@
93
93
  "eslint-config-prettier": "^10.1.8",
94
94
  "eslint-plugin-prettier": "^5.5.3",
95
95
  "prettier": "^3.6.2",
96
- "puppeteer": "^24.16.1",
97
- "rimraf": "^5.0.5",
96
+ "rimraf": "^6.0.1",
98
97
  "typescript": "^5.0.4",
99
98
  "vite": "^7.1.2",
100
99
  "vitest": "^3.2.4"