@vizzly-testing/cli 0.36.0 → 0.38.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.
@@ -64,6 +64,8 @@ export function buildApiBuildPayload(options, comparisonConfig = null) {
64
64
  environment: options.environment || 'test',
65
65
  commit_sha: options.commit,
66
66
  commit_message: options.message,
67
+ commit_author_name: options.commitAuthorName,
68
+ commit_author_email: options.commitAuthorEmail,
67
69
  github_pull_request_number: options.pullRequestNumber,
68
70
  parallel_id: options.parallelId
69
71
  };
@@ -165,6 +165,8 @@ export interface UploadOptions {
165
165
  branch?: string;
166
166
  commit?: string;
167
167
  message?: string;
168
+ commitAuthorName?: string;
169
+ commitAuthorEmail?: string;
168
170
  environment?: string;
169
171
  threshold?: number;
170
172
  minClusterSize?: number;
@@ -517,6 +519,8 @@ export interface BuildOptions {
517
519
  commit_sha?: string;
518
520
  message?: string;
519
521
  commit_message?: string;
522
+ commitAuthorName?: string;
523
+ commitAuthorEmail?: string;
520
524
  environment?: string;
521
525
  threshold?: number;
522
526
  eager?: boolean;
@@ -154,6 +154,8 @@ export function buildBuildInfo(options, defaultBranch = 'main') {
154
154
  branch: options.branch || defaultBranch || 'main',
155
155
  commit_sha: options.commit,
156
156
  commit_message: options.message,
157
+ commit_author_name: options.commitAuthorName,
158
+ commit_author_email: options.commitAuthorEmail,
157
159
  environment: options.environment || 'production',
158
160
  threshold: options.threshold,
159
161
  github_pull_request_number: options.pullRequestNumber,
@@ -156,13 +156,23 @@ export function getCommit() {
156
156
  }
157
157
 
158
158
  /**
159
- * Get the commit message from CI environment variables
159
+ * Get the commit message from CI environment variables.
160
+ *
161
+ * For GitHub Actions pull_request events, the checkout is a synthetic merge
162
+ * commit whose message is "Merge <sha> into <sha>", so the PR title from the
163
+ * event payload is used instead.
164
+ *
160
165
  * @returns {string|null} Commit message or null if not available
161
166
  */
162
167
  export function getCommitMessage() {
163
- return process.env.VIZZLY_COMMIT_MESSAGE ||
164
- // Vizzly override
165
- process.env.CI_COMMIT_MESSAGE ||
168
+ if (process.env.VIZZLY_COMMIT_MESSAGE) {
169
+ return process.env.VIZZLY_COMMIT_MESSAGE;
170
+ }
171
+ if (process.env.GITHUB_ACTIONS) {
172
+ let title = getGitHubEvent().pull_request?.title;
173
+ if (title) return title;
174
+ }
175
+ return process.env.CI_COMMIT_MESSAGE ||
166
176
  // GitLab CI
167
177
  process.env.TRAVIS_COMMIT_MESSAGE ||
168
178
  // Travis CI
@@ -176,6 +186,35 @@ export function getCommitMessage() {
176
186
  // Generic
177
187
  null;
178
188
  }
189
+
190
+ /**
191
+ * Parse a "Name <email>" author string (GitLab's CI_COMMIT_AUTHOR format)
192
+ * @param {string|undefined} value - Author string
193
+ * @returns {{ name: string|null, email: string|null }}
194
+ */
195
+ function parseAuthorString(value) {
196
+ let match = value?.match(/^(.*?)\s*<([^>]*)>\s*$/);
197
+ if (!match) return {
198
+ name: value?.trim() || null,
199
+ email: null
200
+ };
201
+ return {
202
+ name: match[1] || null,
203
+ email: match[2] || null
204
+ };
205
+ }
206
+
207
+ /**
208
+ * Get the commit author from CI environment variables
209
+ * @returns {{ name: string|null, email: string|null }} Author name and email
210
+ */
211
+ export function getCommitAuthor() {
212
+ let gitlab = parseAuthorString(process.env.CI_COMMIT_AUTHOR);
213
+ return {
214
+ name: process.env.VIZZLY_COMMIT_AUTHOR_NAME || gitlab.name || null,
215
+ email: process.env.VIZZLY_COMMIT_AUTHOR_EMAIL || gitlab.email || null
216
+ };
217
+ }
179
218
  function parsePullRequestNumber(value) {
180
219
  if (!/^\d+$/.test(value)) {
181
220
  return null;
package/dist/utils/git.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { execFile } from 'node:child_process';
2
2
  import { promisify } from 'node:util';
3
- import { getBranch as getCIBranch, getCommit as getCICommit, getCommitMessage as getCICommitMessage, getPullRequestNumber } from './ci-env.js';
3
+ import { getBranch as getCIBranch, getCommit as getCICommit, getCommitAuthor as getCICommitAuthor, getCommitMessage as getCICommitMessage, getPullRequestNumber } from './ci-env.js';
4
4
  let execFileAsync = promisify(execFile);
5
5
  async function runGit(args, cwd = process.cwd()) {
6
6
  let {
@@ -124,8 +124,48 @@ export async function detectCommitMessage(override = null, cwd = process.cwd())
124
124
  let ciCommitMessage = getCICommitMessage();
125
125
  if (ciCommitMessage) return ciCommitMessage;
126
126
 
127
- // Fallback to regular git log
128
- return await getCommitMessage(cwd);
127
+ // Fallback to git, reading the same commit reported as commit_sha
128
+ return await readCommit('%B', cwd);
129
+ }
130
+
131
+ /**
132
+ * Read a formatted field from the detected build commit.
133
+ *
134
+ * Uses detectCommit() so CI checkouts (e.g. GitHub's synthetic PR merge
135
+ * commit) describe the PR head rather than HEAD. Falls back to HEAD when that
136
+ * commit is not in local history (shallow clones).
137
+ *
138
+ * @param {string} format - git log --format string
139
+ * @param {string} cwd - Working directory
140
+ * @returns {Promise<string|null>} Formatted output or null
141
+ */
142
+ async function readCommit(format, cwd = process.cwd()) {
143
+ let sha = await detectCommit(null, cwd);
144
+ let refs = /^[0-9a-f]{4,64}$/i.test(sha || '') ? [sha, 'HEAD'] : ['HEAD'];
145
+ for (let ref of refs) {
146
+ try {
147
+ return await runGit(['log', '-1', `--format=${format}`, ref], cwd);
148
+ } catch {
149
+ // Commit not available locally, try the next ref
150
+ }
151
+ }
152
+ return null;
153
+ }
154
+
155
+ /**
156
+ * Detect commit author with environment variable support, falling back to git
157
+ * @param {string} cwd - Working directory
158
+ * @returns {Promise<{ name: string|null, email: string|null }>}
159
+ */
160
+ export async function detectCommitAuthor(cwd = process.cwd()) {
161
+ let author = getCICommitAuthor();
162
+ if (author.name && author.email) return author;
163
+ let stdout = await readCommit('%an%x00%ae', cwd);
164
+ let [gitName, gitEmail] = stdout ? stdout.split('\0') : [];
165
+ return {
166
+ name: author.name || gitName || null,
167
+ email: author.email || gitEmail || null
168
+ };
129
169
  }
130
170
 
131
171
  /**
@@ -206,9 +246,9 @@ export async function detectCommit(override = null, cwd = process.cwd()) {
206
246
  */
207
247
  export async function generateBuildNameWithGit(override = null, cwd = process.cwd()) {
208
248
  if (override) return override;
209
- let branch = await getCurrentBranch(cwd);
210
- let shortSha = await getCurrentCommitSha(cwd);
211
- if (branch && shortSha) {
249
+ let branch = await detectBranch(null, cwd);
250
+ let shortSha = await detectCommit(null, cwd);
251
+ if (branch && branch !== 'unknown' && shortSha) {
212
252
  let shortCommit = shortSha.substring(0, 7);
213
253
  return `${branch}-${shortCommit}`;
214
254
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizzly-testing/cli",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
4
4
  "description": "Visual regression testing from your terminal",
5
5
  "keywords": [
6
6
  "llm-context",
@@ -72,17 +72,18 @@
72
72
  "dev:reporter": "cd src/reporter && vite --config vite.dev.config.js",
73
73
  "test:types": "tsd",
74
74
  "prepublishOnly": "pnpm run build",
75
- "test": "node --experimental-test-coverage --test --test-concurrency=1 --test-reporter=spec $(find tests -name '*.test.js')",
75
+ "test": "node --experimental-test-coverage --test --test-concurrency=1 --test-reporter=spec $(find tests clients/swift/tests-js -name '*.test.js')",
76
76
  "test:watch": "node --test --test-reporter=spec --watch $(find tests -name '*.test.js')",
77
77
  "test:reporter": "playwright test --config=tests/reporter/playwright.config.js",
78
78
  "test:reporter:visual": "node bin/vizzly.js tdd run \"pnpm run test:reporter\" --no-open",
79
79
  "test:swift:e2e": "pnpm run build && node clients/swift/scripts/run-e2e.js",
80
+ "test:swift:previews:e2e": "node clients/swift/scripts/run-preview-e2e.js",
80
81
  "test:tui": "node --test --test-reporter=spec tests/tui/*.test.js",
81
82
  "test:tui:docker": "./tests/tui/run-tui-tests.sh",
82
- "lint": "biome check src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin",
83
- "lint:fix": "biome check --write --unsafe src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin",
84
- "format": "biome format --write src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin",
85
- "format:check": "biome format src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin",
83
+ "lint": "biome check src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin clients/swift/src clients/swift/tests-js clients/swift/scripts",
84
+ "lint:fix": "biome check --write --unsafe src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin clients/swift/src clients/swift/tests-js clients/swift/scripts",
85
+ "format": "biome format --write src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin clients/swift/src clients/swift/tests-js clients/swift/scripts",
86
+ "format:check": "biome format src tests clients/storybook/src clients/storybook/tests clients/static-site/src clients/static-site/tests clients/vitest/src clients/vitest/tests clients/ember/src clients/ember/tests clients/ember/bin clients/swift/src clients/swift/tests-js clients/swift/scripts",
86
87
  "fix": "pnpm run format && pnpm run lint:fix"
87
88
  },
88
89
  "engines": {
@@ -94,7 +95,7 @@
94
95
  },
95
96
  "dependencies": {
96
97
  "@vizzly-testing/bear-den": "0.1.4",
97
- "@vizzly-testing/honeydiff": "^0.16.0",
98
+ "@vizzly-testing/honeydiff": "^0.17.0",
98
99
  "ansis": "^4.2.0",
99
100
  "commander": "^15.0.0",
100
101
  "cosmiconfig": "^9.0.0",
@@ -20,21 +20,29 @@ test workflow in charge of how the UI is exercised.
20
20
 
21
21
  ## Inspect And Verify
22
22
 
23
+ For cloud API queries, selectable fields, or an authorized review decision,
24
+ start with `vizzly api schema --json` and follow
25
+ [schema discovery](references/cli-context.md#query-the-cloud-api).
26
+ Discover request details as needed instead of loading the entire OpenAPI document.
27
+ Use `vizzly context ... --json` for local evidence and human-readable `context`
28
+ output for guided inspection.
29
+
23
30
  1. Choose the supplied cloud build or comparison when one is named. Otherwise,
24
31
  use current local evidence or find the relevant cloud build.
25
- 2. Request bounded JSON:
32
+ 2. For cloud data, discover the relevant API operations. For locally saved data,
33
+ request JSON context:
26
34
 
27
35
  ```bash
28
- vizzly context build current --source local --agent --json
29
- vizzly context build <build-id> --source cloud --agent --json
36
+ vizzly api schema --json
37
+ vizzly context build current --source local --json
30
38
  ```
31
39
 
32
40
  3. Confirm the build, source, branch, timestamps, baseline, review state, and
33
- pagination before drawing conclusions. If `has_more` is true, run the
34
- returned next-page command before concluding. Missing fields remain unknown.
35
- 4. Follow `suggested_commands` to inspect a comparison. View its baseline,
36
- current, and diff images together. If an image cannot be opened, label the
37
- result metadata-only; do not call it visual verification.
41
+ pagination before drawing conclusions. Follow the schema's page instructions
42
+ for cloud data. Missing fields remain unknown.
43
+ 4. For cloud data, discover the comparison and image operations from the schema.
44
+ View the baseline, current, and diff images together. If an image cannot be
45
+ opened, label the result metadata-only; do not call it visual verification.
38
46
  5. Read image dimensions, viewport, browser, diff regions, fingerprint, and
39
47
  relevant history alongside the images. A prior approval is supporting
40
48
  evidence, not permission to approve the current comparison.
@@ -57,7 +65,7 @@ test workflow in charge of how the UI is exercised.
57
65
  ## Load A Reference When Needed
58
66
 
59
67
  - [CLI context](references/cli-context.md): local and cloud evidence, build
60
- discovery, drill-downs, images, and TDD lifecycle.
68
+ discovery, schema queries, review decisions, images, and TDD lifecycle.
61
69
  - [SDK capture](references/sdks.md): add or change screenshot capture code.
62
70
  - [Dynamic content](references/dynamic-content.md): investigate unstable
63
71
  content and screenshot-specific tolerances.
@@ -4,6 +4,41 @@ Use the repository's established CLI invocation and existing authentication.
4
4
  If cloud authentication is unavailable, report the blocker. Do not start an
5
5
  interactive login unless setup is in scope.
6
6
 
7
+ ## Query The Cloud API
8
+
9
+ Discover the live review API instead of guessing endpoints or adding command flags:
10
+
11
+ ```bash
12
+ vizzly api schema --json
13
+ vizzly api schema <operation-id> --json
14
+ vizzly api schema <operation-id> -q view=fields --json
15
+ vizzly api schema <operation-id> -q view=response --json
16
+ ```
17
+
18
+ The index lists available operations. The default operation view describes the
19
+ method, path, parameters, authentication, and example arguments. Request field
20
+ choices or response types only when needed. API JSON payloads are under
21
+ `data.response`.
22
+
23
+ Call the discovered path using `vizzly api <path>`, `-X` for its method, `-H`
24
+ for headers, and `-q` for query parameters. Send the discovered API version
25
+ header on data requests. Use `fields` to select just the evidence needed.
26
+ Follow the response's pagination values explicitly; the CLI fetches one page
27
+ per request. Keep cursors opaque and preserve the query they belong to.
28
+
29
+ For a review, discover projects/builds, then inspect the build's screenshots,
30
+ comparisons, and image endpoints. Download images with `--output <new-file>`
31
+ and view baseline, current, and diff together. Use file output for large analysis
32
+ responses too. Existing files are not overwritten. Export the full schema only
33
+ when needed: `vizzly api schema --full --output <new-file> --json`.
34
+
35
+ Review decisions require an authorized task, user credentials, and the documented
36
+ organization header. Discover the decision operation's body before sending it
37
+ with `-d @<file>` or `-d @-` for stdin. Generate a fresh `commandId` for each
38
+ intended decision, then read back the review state. Generic writes are not
39
+ automatically replayed; after an uncertain result, inspect state before retrying.
40
+ Treat schema examples as argument arrays, not shell scripts.
41
+
7
42
  ## Choose The Evidence
8
43
 
9
44
  Use an ID supplied by the task. If no cloud build is supplied, list recent
@@ -12,17 +47,18 @@ builds and select the one matching the branch, commit, or pull request:
12
47
  ```bash
13
48
  vizzly builds --branch <branch> --limit 5 --json
14
49
  vizzly status <build-id> --json
15
- vizzly context build <build-id> --source cloud --agent --json
50
+ vizzly api schema sdk.getBuildContext --json
16
51
  ```
17
52
 
18
- Use status for lifecycle facts and build context for visual evidence. Do not
19
- assume the first returned comparison is the most important; preserve API order
20
- and inspect the records relevant to the task.
53
+ Use status for lifecycle facts, then follow the discovered build-context
54
+ operation for visual evidence. Do not assume the first returned comparison is
55
+ the most important; preserve API order and inspect the records relevant to the
56
+ task.
21
57
 
22
58
  For saved local evidence:
23
59
 
24
60
  ```bash
25
- vizzly context build current --source local --agent --json
61
+ vizzly context build current --source local --json
26
62
  vizzly context screenshot "<screenshot-name>" --source local --json
27
63
  vizzly context review-queue --source local --json
28
64
  ```
@@ -50,20 +86,45 @@ vizzly tdd stop --json
50
86
  `tdd run` and `tdd start` are alternatives. Stop only a daemon started for the
51
87
  current task.
52
88
 
89
+ ## Review A Local TDD Run
90
+
91
+ After `tdd run` exits, inspect captures directly from `.vizzly`. These
92
+ commands don't need a running TDD daemon or cloud credentials:
93
+
94
+ ```bash
95
+ vizzly tdd screenshots list --page 1 --page-size 20
96
+ vizzly tdd screenshots latest
97
+ vizzly tdd screenshots show <comparison-id>
98
+ ```
99
+
100
+ `latest` prints the current screenshot path for the most recent capture. Use
101
+ `list` to find a comparison ID when you need a specific screenshot or browser
102
+ variant. During UI iteration, inspect the current image first; request
103
+ `--image diff` or `--image all` when comparison diagnostics help answer the
104
+ question. Open the printed image paths with the available image viewer.
105
+
106
+ `accept` replaces one local baseline and updates that report entry. Use it only
107
+ when the task explicitly authorizes accepting a baseline; don't accept a change
108
+ just to make a diff disappear:
109
+
110
+ ```bash
111
+ vizzly tdd screenshots accept <comparison-id>
112
+ ```
113
+
53
114
  When a cloud build is in scope:
54
115
 
55
116
  ```bash
56
117
  vizzly run "<existing visual test command>" --wait --json
57
- vizzly context build <build-id> --source cloud --agent --json
118
+ vizzly api schema --json
58
119
  ```
59
120
 
60
121
  ## Inspect A Comparison
61
122
 
62
- Follow the build response's `suggested_commands`. The direct form is:
123
+ Discover comparison and image operations from the schema:
63
124
 
64
125
  ```bash
65
- vizzly context comparison <comparison-id> --source <local-or-cloud> --agent --json
66
- vizzly context comparison <comparison-id> --source <local-or-cloud> --agent --include diffs --json
126
+ vizzly api schema getComparisonContext --json
127
+ vizzly api schema sdk.getComparisonImage --json
67
128
  ```
68
129
 
69
130
  Open all three images together. Prefer `original_url` and fall back to `url`:
@@ -86,12 +147,10 @@ vizzly context similar <fingerprint-hash> --source cloud --json
86
147
  vizzly context review-queue --source <local-or-cloud> --json
87
148
  ```
88
149
 
89
- Use `--include diffs` only when compact diagnostics are insufficient. Request
90
- comments only when human review context matters.
150
+ Use the documented response and pagination fields to request only the evidence
151
+ needed. Request comments only when human review context matters.
91
152
 
92
153
  ## Continue Without Guessing
93
154
 
94
- Run returned `suggested_commands` rather than reconstructing IDs, sources, or
95
- pagination. When more evidence exists, the next-page command carries the API's
96
- opaque `--cursor`; do not edit or interpret it. Keep follow-up commands pinned
97
- to the source that produced the evidence.
155
+ Use IDs and pagination values from API responses. Keep cursors opaque and
156
+ preserve the query they belong to when requesting the next page.
@@ -36,8 +36,8 @@ vizzly finalize "<shared-ci-run-id>" --json
36
36
  - `vizzly doctor`: local configuration
37
37
  - `vizzly tdd status --json`: a local daemon started by the task
38
38
  - `vizzly status <build-id> --json`: cloud lifecycle
39
- - `vizzly context build <build-id> --source cloud --agent --json`: visual
40
- evidence
39
+ - `vizzly api schema --json`: discover supported cloud review operations and
40
+ their request and response schemas.
41
41
 
42
42
  If screenshots are absent, verify the existing integration, the test path that
43
43
  should capture them, and the active session. If authentication is absent,