@vizzly-testing/cli 0.20.1 → 0.21.1
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/dist/commands/run.js +2 -0
- package/dist/commands/tdd.js +10 -2
- package/dist/reporter/reporter-bundle.iife.js +50 -50
- package/dist/server/http-server.js +4 -1
- package/dist/server/routers/baseline.js +43 -2
- package/dist/server/routers/dashboard.js +21 -36
- package/dist/server/routers/events.js +134 -0
- package/dist/services/auth-service.js +117 -0
- package/dist/services/project-service.js +136 -0
- package/dist/tdd/tdd-service.js +251 -0
- package/docs/internal/SDK-API.md +1018 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1018 @@
|
|
|
1
|
+
# Vizzly SDK API Documentation
|
|
2
|
+
|
|
3
|
+
This document describes the public API endpoints available for Vizzly SDKs and CLI tools.
|
|
4
|
+
|
|
5
|
+
## Base URL
|
|
6
|
+
|
|
7
|
+
All SDK endpoints are prefixed with `/api/sdk/`
|
|
8
|
+
|
|
9
|
+
## Authentication
|
|
10
|
+
|
|
11
|
+
All SDK endpoints require:
|
|
12
|
+
|
|
13
|
+
- **API Token**: Passed via `Authorization: Bearer <token>` header
|
|
14
|
+
- **User-Agent**: Must contain "Vizzly" in the header value (e.g., `Vizzly-CLI/1.0.0`)
|
|
15
|
+
|
|
16
|
+
## Build Management
|
|
17
|
+
|
|
18
|
+
### Create Build
|
|
19
|
+
|
|
20
|
+
Creates a new build for screenshot uploads and comparisons. Supports both standalone builds and parallel execution for sharded test runs.
|
|
21
|
+
|
|
22
|
+
**Endpoint:** `POST /api/sdk/builds`
|
|
23
|
+
|
|
24
|
+
**Request Body:**
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"build": {
|
|
29
|
+
"name": "CLI Build 2024-01-15T10:30:00Z",
|
|
30
|
+
"branch": "feature/new-ui",
|
|
31
|
+
"commit_sha": "abc123def456",
|
|
32
|
+
"commit_message": "Add new user dashboard",
|
|
33
|
+
"common_ancestor_sha": "def456ghi789",
|
|
34
|
+
"environment": "test",
|
|
35
|
+
"github_pull_request_number": 123,
|
|
36
|
+
|
|
37
|
+
// Parallel execution fields (optional)
|
|
38
|
+
"parallel_id": "workflow-12345-attempt-1"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Parallel Execution Field:**
|
|
44
|
+
|
|
45
|
+
- `parallel_id` (optional): Unique identifier for parallel test execution (e.g., GitHub workflow run ID)
|
|
46
|
+
|
|
47
|
+
When `parallel_id` is provided:
|
|
48
|
+
|
|
49
|
+
- **First shard**: Creates a new build with the parallel_id
|
|
50
|
+
- **Subsequent shards**: Find and reuse the existing build with the same parallel_id
|
|
51
|
+
- **All shards**: Upload screenshots to the same build (leveraging existing build reopening logic)
|
|
52
|
+
|
|
53
|
+
**Response (201):**
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"id": "build-uuid",
|
|
58
|
+
"name": "CLI Build 2024-01-15T10:30:00Z",
|
|
59
|
+
"branch": "feature/new-ui",
|
|
60
|
+
"environment": "test",
|
|
61
|
+
"status": "created",
|
|
62
|
+
"url": "https://app.vizzly.co/org-slug/project-slug/builds/build-uuid",
|
|
63
|
+
"organizationSlug": "my-org",
|
|
64
|
+
"projectSlug": "my-project",
|
|
65
|
+
"created_at": "2024-01-15T10:30:00Z"
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Get Build Details
|
|
70
|
+
|
|
71
|
+
Retrieves build information with optional related data.
|
|
72
|
+
|
|
73
|
+
**Endpoint:** `GET /api/sdk/builds/:buildId`
|
|
74
|
+
|
|
75
|
+
**Query Parameters:**
|
|
76
|
+
|
|
77
|
+
- `include` (optional): `"screenshots"`, `"comparisons"`, or `"review-summary"` to include related data
|
|
78
|
+
|
|
79
|
+
**Response (200):**
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"build": {
|
|
84
|
+
"id": "build-uuid",
|
|
85
|
+
"name": "CLI Build 2024-01-15T10:30:00Z",
|
|
86
|
+
"branch": "feature/new-ui",
|
|
87
|
+
"status": "completed",
|
|
88
|
+
"screenshot_count": 25,
|
|
89
|
+
"comparison_count": 20,
|
|
90
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
91
|
+
"screenshots": [
|
|
92
|
+
// Only included if include=screenshots
|
|
93
|
+
{
|
|
94
|
+
"id": "screenshot-uuid",
|
|
95
|
+
"name": "homepage-desktop-chrome",
|
|
96
|
+
"build_id": "build-uuid",
|
|
97
|
+
"browser": "chrome",
|
|
98
|
+
"viewport_width": 1920,
|
|
99
|
+
"viewport_height": 1080,
|
|
100
|
+
"url": "https://example.com/page",
|
|
101
|
+
"sha256": "abc123def456...",
|
|
102
|
+
"status": "completed",
|
|
103
|
+
"original_url": "https://storage.example.com/screenshot.png",
|
|
104
|
+
"file_size_bytes": 245760,
|
|
105
|
+
"width": 1920,
|
|
106
|
+
"height": 1080,
|
|
107
|
+
"created_at": "2024-01-15T10:30:00Z"
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"comparisons": [], // Only included if include=comparisons
|
|
111
|
+
"review_summary": {
|
|
112
|
+
// Only included if include=review-summary
|
|
113
|
+
"build_id": "build-uuid",
|
|
114
|
+
"review_status": "in_progress",
|
|
115
|
+
"assignments": [
|
|
116
|
+
{
|
|
117
|
+
"id": "assignment-uuid",
|
|
118
|
+
"user_id": "user-uuid",
|
|
119
|
+
"user_name": "Jane Smith",
|
|
120
|
+
"status": "pending",
|
|
121
|
+
"assigned_at": "2024-01-15T10:00:00Z"
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"statistics": {
|
|
125
|
+
"total_assignments": 1,
|
|
126
|
+
"pending": 1,
|
|
127
|
+
"completed": 0
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### List Builds
|
|
135
|
+
|
|
136
|
+
Retrieves a paginated list of builds with optional filtering.
|
|
137
|
+
|
|
138
|
+
**Endpoint:** `GET /api/sdk/builds`
|
|
139
|
+
|
|
140
|
+
**Query Parameters:**
|
|
141
|
+
|
|
142
|
+
- `limit` (optional, default: 20): Maximum number of builds to return
|
|
143
|
+
- `offset` (optional, default: 0): Number of builds to skip
|
|
144
|
+
- `branch` (optional): Filter by branch name
|
|
145
|
+
- `environment` (optional): Filter by environment
|
|
146
|
+
- `status` (optional): Filter by build status
|
|
147
|
+
|
|
148
|
+
**Response (200):**
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"builds": [
|
|
153
|
+
{
|
|
154
|
+
"id": "build-uuid",
|
|
155
|
+
"name": "CLI Build 2024-01-15T10:30:00Z",
|
|
156
|
+
"branch": "main",
|
|
157
|
+
"environment": "test",
|
|
158
|
+
"status": "completed",
|
|
159
|
+
"created_at": "2024-01-15T10:30:00Z"
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
"pagination": {
|
|
163
|
+
"total": 150,
|
|
164
|
+
"limit": 20,
|
|
165
|
+
"offset": 0,
|
|
166
|
+
"hasMore": true
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Update Build Status
|
|
172
|
+
|
|
173
|
+
Updates the status of a build (typically called by CLI when test suite completes).
|
|
174
|
+
|
|
175
|
+
**Endpoint:** `PUT /api/sdk/builds/:buildId/status`
|
|
176
|
+
|
|
177
|
+
**Request Body:**
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"status": "completed",
|
|
182
|
+
"executionTimeMs": 45000
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Valid Status Values:**
|
|
187
|
+
|
|
188
|
+
- `"pending"` → Maps to "created" (build ready for screenshots)
|
|
189
|
+
- `"running"` → Maps to "processing" (build is processing screenshots)
|
|
190
|
+
- `"completed"` → Maps to "completed" (all screenshots processed successfully)
|
|
191
|
+
- `"failed"` → Maps to "failed" (build failed or was terminated)
|
|
192
|
+
|
|
193
|
+
**Important: Parallel Build Behavior**
|
|
194
|
+
|
|
195
|
+
For builds with a `parallel_id`, the `"completed"` status is **not allowed** via this endpoint. Instead, you'll receive:
|
|
196
|
+
|
|
197
|
+
**Response for Parallel Build Completion Attempt (200):**
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"build_id": "build-uuid",
|
|
202
|
+
"status": "processing",
|
|
203
|
+
"message": "Build with parallel_id cannot be finalized via status endpoint. Use POST /api/sdk/parallel/:parallelId/finalize to complete all parallel shards.",
|
|
204
|
+
"parallel_id": "workflow-12345-attempt-1",
|
|
205
|
+
"awaiting_finalize_all": true
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Response for Regular Builds (200):**
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{
|
|
213
|
+
"message": "Build status updated successfully",
|
|
214
|
+
"build": {
|
|
215
|
+
"id": "build-uuid",
|
|
216
|
+
"status": "completed",
|
|
217
|
+
"execution_time_ms": 45000
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Screenshot Management
|
|
223
|
+
|
|
224
|
+
### Upload Screenshot (Real-time)
|
|
225
|
+
|
|
226
|
+
Uploads a single screenshot to an existing build (used during test execution).
|
|
227
|
+
|
|
228
|
+
**Endpoint:** `POST /api/sdk/builds/:buildId/screenshots`
|
|
229
|
+
|
|
230
|
+
**Request Body:**
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"name": "homepage-desktop-chrome",
|
|
235
|
+
"image_data": "base64-encoded-image-data",
|
|
236
|
+
"properties": {
|
|
237
|
+
"browser": "chrome",
|
|
238
|
+
"browserVersion": "91.0.4472.124",
|
|
239
|
+
"device": "desktop",
|
|
240
|
+
"viewport": {
|
|
241
|
+
"width": 1920,
|
|
242
|
+
"height": 1080
|
|
243
|
+
},
|
|
244
|
+
"url": "https://example.com/homepage",
|
|
245
|
+
"selector": ".main-content"
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Response (201):**
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"id": "screenshot-uuid",
|
|
255
|
+
"name": "homepage-desktop-chrome",
|
|
256
|
+
"build_id": "build-uuid",
|
|
257
|
+
"status": "pending",
|
|
258
|
+
"sha256": "abc123...",
|
|
259
|
+
"isDuplicate": false,
|
|
260
|
+
"build_screenshot_count": 5,
|
|
261
|
+
"created_at": "2024-01-15T10:30:00Z"
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Check Existing Screenshots (Signature-Based Deduplication)
|
|
266
|
+
|
|
267
|
+
Checks if screenshots with given SHA256 hashes already exist using intelligent signature-based deduplication. Supports both legacy SHA-only format and new signature-based format.
|
|
268
|
+
|
|
269
|
+
**Endpoint:** `POST /api/sdk/check-shas`
|
|
270
|
+
|
|
271
|
+
#### Legacy Format (SHA-only)
|
|
272
|
+
|
|
273
|
+
**Request Body:**
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"buildId": "build-uuid",
|
|
278
|
+
"shas": ["abc123def456...", "def456ghi789..."]
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
**Response (200):**
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"existing": ["abc123def456..."],
|
|
287
|
+
"missing": ["def456ghi789..."],
|
|
288
|
+
"summary": {
|
|
289
|
+
"total_checked": 2,
|
|
290
|
+
"existing_count": 1,
|
|
291
|
+
"missing_count": 1,
|
|
292
|
+
"screenshots_created": 1,
|
|
293
|
+
"format_used": "legacy-sha-only"
|
|
294
|
+
},
|
|
295
|
+
"screenshots": [
|
|
296
|
+
{
|
|
297
|
+
"id": "screenshot-uuid",
|
|
298
|
+
"name": "auto-generated-name",
|
|
299
|
+
"sha256": "abc123def456...",
|
|
300
|
+
"fromExisting": true,
|
|
301
|
+
"alreadyExisted": false
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
#### New Format (Signature-Based - Recommended)
|
|
308
|
+
|
|
309
|
+
**Request Body:**
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"buildId": "build-uuid",
|
|
314
|
+
"screenshots": [
|
|
315
|
+
{
|
|
316
|
+
"sha256": "abc123def456...",
|
|
317
|
+
"name": "homepage-login-form",
|
|
318
|
+
"browser": "chrome",
|
|
319
|
+
"viewport_width": 1920,
|
|
320
|
+
"viewport_height": 1080,
|
|
321
|
+
"url": "https://example.com/login",
|
|
322
|
+
"selector": ".login-form"
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"sha256": "def456ghi789...",
|
|
326
|
+
"name": "dashboard-mobile",
|
|
327
|
+
"browser": "chrome",
|
|
328
|
+
"viewport_width": 375,
|
|
329
|
+
"viewport_height": 667
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Response (200):**
|
|
336
|
+
|
|
337
|
+
```json
|
|
338
|
+
{
|
|
339
|
+
"existing": [],
|
|
340
|
+
"missing": ["abc123def456...", "def456ghi789..."],
|
|
341
|
+
"summary": {
|
|
342
|
+
"total_checked": 2,
|
|
343
|
+
"existing_count": 0,
|
|
344
|
+
"missing_count": 2,
|
|
345
|
+
"screenshots_created": 0,
|
|
346
|
+
"format_used": "signature-based"
|
|
347
|
+
},
|
|
348
|
+
"results": [
|
|
349
|
+
{
|
|
350
|
+
"sha256": "abc123def456...",
|
|
351
|
+
"exists": true,
|
|
352
|
+
"reusable": false,
|
|
353
|
+
"reason": "Different signature (homepage-desktop|1920|chrome vs homepage-login-form|1920|chrome)"
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"sha256": "def456ghi789...",
|
|
357
|
+
"exists": false,
|
|
358
|
+
"reusable": false,
|
|
359
|
+
"reason": "SHA not found"
|
|
360
|
+
}
|
|
361
|
+
],
|
|
362
|
+
"screenshots": []
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### Signature-Based Logic
|
|
367
|
+
|
|
368
|
+
The new format uses screenshot **signature** for intelligent deduplication:
|
|
369
|
+
|
|
370
|
+
- **Signature**: `name + viewport_width + browser` (excludes height as it's variable)
|
|
371
|
+
- **Same SHA + Same Signature**: Screenshot is reusable, returns as "existing"
|
|
372
|
+
- **Same SHA + Different Signature**: Screenshot not reusable, returns as "missing" to force new upload
|
|
373
|
+
- **Different SHA**: New screenshot, returns as "missing"
|
|
374
|
+
|
|
375
|
+
This ensures screenshots with new names but identical image data create separate records with correct names.
|
|
376
|
+
|
|
377
|
+
### Batch Upload Screenshots
|
|
378
|
+
|
|
379
|
+
Uploads multiple screenshot files and/or references existing screenshots by SHA256.
|
|
380
|
+
|
|
381
|
+
**Endpoint:** `POST /api/sdk/upload`
|
|
382
|
+
|
|
383
|
+
**Content-Type:** `multipart/form-data`
|
|
384
|
+
|
|
385
|
+
**Form Fields:**
|
|
386
|
+
|
|
387
|
+
- `screenshots` (files): Array of screenshot image files
|
|
388
|
+
- `build_id` (optional): ID of existing build to upload to
|
|
389
|
+
- `build_name` (optional): Name for new build if creating one
|
|
390
|
+
- `branch` (optional): Git branch name
|
|
391
|
+
- `commit_sha` (optional): Git commit SHA
|
|
392
|
+
- `commit_message` (optional): Git commit message
|
|
393
|
+
- `common_ancestor_sha` (optional): Common ancestor commit SHA
|
|
394
|
+
- `environment` (optional, default: "test"): Environment name
|
|
395
|
+
- `existing_shas` (optional): JSON array of SHA256 hashes for existing screenshots
|
|
396
|
+
- `config` (optional): JSON configuration object
|
|
397
|
+
- `threshold` (optional): Comparison threshold
|
|
398
|
+
- `parallel_id` (optional): Unique identifier for parallel test execution
|
|
399
|
+
|
|
400
|
+
**Response (201):**
|
|
401
|
+
|
|
402
|
+
```json
|
|
403
|
+
{
|
|
404
|
+
"message": "Screenshots uploaded successfully and queued for processing",
|
|
405
|
+
"build": {
|
|
406
|
+
"id": "build-uuid",
|
|
407
|
+
"name": "CLI Build",
|
|
408
|
+
"status": "processing"
|
|
409
|
+
},
|
|
410
|
+
"screenshots": [],
|
|
411
|
+
"count": 10,
|
|
412
|
+
"processing_time_ms": 1250,
|
|
413
|
+
"efficiency": {
|
|
414
|
+
"total_processed": 10,
|
|
415
|
+
"files_uploaded": 7,
|
|
416
|
+
"existing_shas_requested": 3,
|
|
417
|
+
"existing_reused": 3,
|
|
418
|
+
"existing_skipped": 0
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Comparison Management
|
|
424
|
+
|
|
425
|
+
### Get Comparison Details
|
|
426
|
+
|
|
427
|
+
Retrieves detailed information about a specific comparison, including diff images and commit context for debugging.
|
|
428
|
+
|
|
429
|
+
**Endpoint:** `GET /api/sdk/comparisons/:comparisonId`
|
|
430
|
+
|
|
431
|
+
**Response (200):**
|
|
432
|
+
|
|
433
|
+
```json
|
|
434
|
+
{
|
|
435
|
+
"comparison": {
|
|
436
|
+
"id": "comparison-uuid",
|
|
437
|
+
"build_id": "build-uuid",
|
|
438
|
+
"build_name": "CLI Build",
|
|
439
|
+
"build_branch": "feature/new-ui",
|
|
440
|
+
"build_commit_sha": "abc123def456",
|
|
441
|
+
"build_commit_message": "✨ Add new dashboard",
|
|
442
|
+
"build_common_ancestor_sha": "def456ghi789",
|
|
443
|
+
"name": "homepage-desktop-chrome",
|
|
444
|
+
"status": "completed",
|
|
445
|
+
"diff_percentage": 2.5,
|
|
446
|
+
"threshold": 0.1,
|
|
447
|
+
"has_diff": true,
|
|
448
|
+
"approval_status": "pending",
|
|
449
|
+
"current_screenshot_url": "https://storage.example.com/current.png",
|
|
450
|
+
"baseline_screenshot_url": "https://storage.example.com/baseline.png",
|
|
451
|
+
"diff_url": "https://storage.example.com/diff.png",
|
|
452
|
+
"created_at": "2024-01-15T10:30:00Z"
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
## Build Collaboration
|
|
458
|
+
|
|
459
|
+
### Create Build Comment
|
|
460
|
+
|
|
461
|
+
Creates a comment on a build for team collaboration and feedback.
|
|
462
|
+
|
|
463
|
+
**Endpoint:** `POST /api/sdk/builds/:buildId/comments`
|
|
464
|
+
|
|
465
|
+
**Request Body:**
|
|
466
|
+
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"content": "All visual diffs look good, approving this build for deployment.",
|
|
470
|
+
"type": "general"
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Comment Types:**
|
|
475
|
+
|
|
476
|
+
- `"general"`: General comment or discussion
|
|
477
|
+
- `"approval"`: Comment indicating approval
|
|
478
|
+
- `"rejection"`: Comment indicating rejection with concerns
|
|
479
|
+
|
|
480
|
+
**Response (201):**
|
|
481
|
+
|
|
482
|
+
```json
|
|
483
|
+
{
|
|
484
|
+
"comment": {
|
|
485
|
+
"id": "comment-uuid",
|
|
486
|
+
"build_id": "build-uuid",
|
|
487
|
+
"user_id": "user-uuid",
|
|
488
|
+
"author_name": "John Doe",
|
|
489
|
+
"content": "All visual diffs look good, approving this build for deployment.",
|
|
490
|
+
"type": "general",
|
|
491
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
492
|
+
"updated_at": "2024-01-15T10:30:00Z"
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### List Build Comments
|
|
498
|
+
|
|
499
|
+
Retrieves all comments for a build, including nested replies.
|
|
500
|
+
|
|
501
|
+
**Endpoint:** `GET /api/sdk/builds/:buildId/comments`
|
|
502
|
+
|
|
503
|
+
**Response (200):**
|
|
504
|
+
|
|
505
|
+
```json
|
|
506
|
+
{
|
|
507
|
+
"comments": [
|
|
508
|
+
{
|
|
509
|
+
"id": "comment-uuid",
|
|
510
|
+
"build_id": "build-uuid",
|
|
511
|
+
"user_id": "user-uuid",
|
|
512
|
+
"author_name": "John Doe",
|
|
513
|
+
"content": "All visual diffs look good, approving this build for deployment.",
|
|
514
|
+
"type": "general",
|
|
515
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
516
|
+
"updated_at": "2024-01-15T10:30:00Z",
|
|
517
|
+
"replies": []
|
|
518
|
+
}
|
|
519
|
+
]
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Note:** For MCP/Claude Code integration, the `profile_photo_url` and `email` fields are filtered out to reduce token usage.
|
|
524
|
+
|
|
525
|
+
## Comparison Approvals
|
|
526
|
+
|
|
527
|
+
### Update Comparison Approval Status
|
|
528
|
+
|
|
529
|
+
Updates the approval status of a comparison.
|
|
530
|
+
|
|
531
|
+
**Endpoint:** `PUT /api/sdk/comparisons/:comparisonId/approval`
|
|
532
|
+
|
|
533
|
+
**Request Body:**
|
|
534
|
+
|
|
535
|
+
```json
|
|
536
|
+
{
|
|
537
|
+
"approval_status": "approved",
|
|
538
|
+
"comment": "Visual changes look intentional, layout improvements are good."
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
**Valid Approval Status Values:**
|
|
543
|
+
|
|
544
|
+
- `"pending"`: No decision made yet (default)
|
|
545
|
+
- `"approved"`: Comparison approved, changes are intentional
|
|
546
|
+
- `"rejected"`: Comparison rejected, changes need investigation
|
|
547
|
+
- `"auto_approved"`: Automatically approved (identical screenshots)
|
|
548
|
+
|
|
549
|
+
**Response (200):**
|
|
550
|
+
|
|
551
|
+
```json
|
|
552
|
+
{
|
|
553
|
+
"message": "Comparison approval status updated successfully",
|
|
554
|
+
"comparison": {
|
|
555
|
+
"id": "comparison-uuid",
|
|
556
|
+
"approval_status": "approved",
|
|
557
|
+
"approval_comment": "Visual changes look intentional, layout improvements are good.",
|
|
558
|
+
"approved_by": "user-uuid",
|
|
559
|
+
"approved_at": "2024-01-15T10:30:00Z"
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Approve Comparison
|
|
565
|
+
|
|
566
|
+
Convenient endpoint to approve a comparison with optional comment.
|
|
567
|
+
|
|
568
|
+
**Endpoint:** `POST /api/sdk/comparisons/:comparisonId/approve`
|
|
569
|
+
|
|
570
|
+
**Request Body:**
|
|
571
|
+
|
|
572
|
+
```json
|
|
573
|
+
{
|
|
574
|
+
"comment": "Homepage redesign looks excellent, ready for production."
|
|
575
|
+
}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Response (200):**
|
|
579
|
+
|
|
580
|
+
```json
|
|
581
|
+
{
|
|
582
|
+
"message": "Comparison approved successfully",
|
|
583
|
+
"comparison": {
|
|
584
|
+
"id": "comparison-uuid",
|
|
585
|
+
"approval_status": "approved",
|
|
586
|
+
"approval_comment": "Homepage redesign looks excellent, ready for production.",
|
|
587
|
+
"approved_by": "user-uuid",
|
|
588
|
+
"approved_at": "2024-01-15T10:30:00Z"
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Reject Comparison
|
|
594
|
+
|
|
595
|
+
Convenient endpoint to reject a comparison with required reason.
|
|
596
|
+
|
|
597
|
+
**Endpoint:** `POST /api/sdk/comparisons/:comparisonId/reject`
|
|
598
|
+
|
|
599
|
+
**Request Body:**
|
|
600
|
+
|
|
601
|
+
```json
|
|
602
|
+
{
|
|
603
|
+
"reason": "Button alignment is off by 2px, needs to be fixed before deployment."
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**Response (200):**
|
|
608
|
+
|
|
609
|
+
```json
|
|
610
|
+
{
|
|
611
|
+
"message": "Comparison rejected successfully",
|
|
612
|
+
"comparison": {
|
|
613
|
+
"id": "comparison-uuid",
|
|
614
|
+
"approval_status": "rejected",
|
|
615
|
+
"approval_comment": "Button alignment is off by 2px, needs to be fixed before deployment.",
|
|
616
|
+
"approved_by": "user-uuid",
|
|
617
|
+
"approved_at": "2024-01-15T10:30:00Z"
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
## Review Management
|
|
623
|
+
|
|
624
|
+
### Get Build Review Summary
|
|
625
|
+
|
|
626
|
+
Retrieves review status, assignments, and statistics for a build.
|
|
627
|
+
|
|
628
|
+
**Endpoint:** `GET /api/sdk/builds/:buildId/review-summary`
|
|
629
|
+
|
|
630
|
+
**Response (200):**
|
|
631
|
+
|
|
632
|
+
```json
|
|
633
|
+
{
|
|
634
|
+
"build_id": "build-uuid",
|
|
635
|
+
"review_status": "in_progress",
|
|
636
|
+
"assignments": [
|
|
637
|
+
{
|
|
638
|
+
"id": "assignment-uuid",
|
|
639
|
+
"user_id": "user-uuid",
|
|
640
|
+
"user_name": "Jane Smith",
|
|
641
|
+
"status": "pending",
|
|
642
|
+
"assigned_at": "2024-01-15T10:00:00Z",
|
|
643
|
+
"due_date": null
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
"id": "assignment-uuid-2",
|
|
647
|
+
"user_id": "user-uuid-2",
|
|
648
|
+
"user_name": "John Doe",
|
|
649
|
+
"status": "completed",
|
|
650
|
+
"assigned_at": "2024-01-15T10:00:00Z",
|
|
651
|
+
"completed_at": "2024-01-15T10:30:00Z",
|
|
652
|
+
"due_date": null
|
|
653
|
+
}
|
|
654
|
+
],
|
|
655
|
+
"statistics": {
|
|
656
|
+
"total_assignments": 2,
|
|
657
|
+
"pending": 1,
|
|
658
|
+
"completed": 1,
|
|
659
|
+
"approved": 0,
|
|
660
|
+
"rejected": 0
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
**Review Status Values:**
|
|
666
|
+
|
|
667
|
+
- `"pending"`: No reviewers assigned yet
|
|
668
|
+
- `"in_progress"`: Reviewers assigned, waiting for completion
|
|
669
|
+
- `"completed"`: All reviewers have completed their review
|
|
670
|
+
- `"approved"`: Build approved by all reviewers
|
|
671
|
+
- `"rejected"`: Build rejected by one or more reviewers
|
|
672
|
+
|
|
673
|
+
## Token Management
|
|
674
|
+
|
|
675
|
+
### Get Token Context
|
|
676
|
+
|
|
677
|
+
Retrieves information about the current API token and associated organization/project.
|
|
678
|
+
|
|
679
|
+
**Endpoint:** `GET /api/sdk/token/context`
|
|
680
|
+
|
|
681
|
+
**Response (200):**
|
|
682
|
+
|
|
683
|
+
```json
|
|
684
|
+
{
|
|
685
|
+
"token": {
|
|
686
|
+
"id": "token-uuid",
|
|
687
|
+
"name": "CI/CD Token",
|
|
688
|
+
"prefix": "vzy_"
|
|
689
|
+
},
|
|
690
|
+
"organization": {
|
|
691
|
+
"id": "org-uuid",
|
|
692
|
+
"name": "My Company",
|
|
693
|
+
"slug": "my-company"
|
|
694
|
+
},
|
|
695
|
+
"project": {
|
|
696
|
+
"id": "project-uuid",
|
|
697
|
+
"name": "My App",
|
|
698
|
+
"slug": "my-app"
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
## Error Responses
|
|
704
|
+
|
|
705
|
+
All endpoints may return these common error responses:
|
|
706
|
+
|
|
707
|
+
### 400 Bad Request
|
|
708
|
+
|
|
709
|
+
```json
|
|
710
|
+
{
|
|
711
|
+
"error": "Validation error message"
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### 400 Bad Request (Missing User-Agent)
|
|
716
|
+
|
|
717
|
+
```json
|
|
718
|
+
{
|
|
719
|
+
"error": "User-Agent header required"
|
|
720
|
+
}
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
```json
|
|
724
|
+
{
|
|
725
|
+
"error": "Invalid User-Agent for SDK request"
|
|
726
|
+
}
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### 401 Unauthorized
|
|
730
|
+
|
|
731
|
+
```json
|
|
732
|
+
{
|
|
733
|
+
"error": "Invalid or missing API token"
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### 404 Not Found
|
|
738
|
+
|
|
739
|
+
```json
|
|
740
|
+
{
|
|
741
|
+
"error": "Resource not found"
|
|
742
|
+
}
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
### 413 Payload Too Large (Storage Quota Exceeded)
|
|
746
|
+
|
|
747
|
+
```json
|
|
748
|
+
{
|
|
749
|
+
"error": "Storage quota exceeded",
|
|
750
|
+
"details": {
|
|
751
|
+
"currentUsage": "2.5GB",
|
|
752
|
+
"limit": "5.0GB",
|
|
753
|
+
"estimatedUpload": "3.0GB",
|
|
754
|
+
"planName": "Pro"
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### 500 Internal Server Error
|
|
760
|
+
|
|
761
|
+
```json
|
|
762
|
+
{
|
|
763
|
+
"error": "Internal server error message"
|
|
764
|
+
}
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
## Rate Limits
|
|
768
|
+
|
|
769
|
+
Currently, there are no enforced rate limits on SDK endpoints, but reasonable usage is expected.
|
|
770
|
+
|
|
771
|
+
## Usage Examples
|
|
772
|
+
|
|
773
|
+
### Complete Workflow Example
|
|
774
|
+
|
|
775
|
+
1. **Create a build:**
|
|
776
|
+
|
|
777
|
+
```bash
|
|
778
|
+
curl -X POST /api/sdk/builds \
|
|
779
|
+
-H "Authorization: Bearer your-api-token" \
|
|
780
|
+
-H "User-Agent: Vizzly-CLI/1.0.0" \
|
|
781
|
+
-H "Content-Type: application/json" \
|
|
782
|
+
-d '{"build": {"name": "Test Build", "branch": "main"}}'
|
|
783
|
+
```
|
|
784
|
+
|
|
785
|
+
2. **Upload screenshots:**
|
|
786
|
+
|
|
787
|
+
```bash
|
|
788
|
+
curl -X POST /api/sdk/builds/build-uuid/screenshots \
|
|
789
|
+
-H "Authorization: Bearer your-api-token" \
|
|
790
|
+
-H "User-Agent: Vizzly-CLI/1.0.0" \
|
|
791
|
+
-H "Content-Type: application/json" \
|
|
792
|
+
-d '{"name": "homepage", "image_data": "base64..."}'
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
3. **Complete the build:**
|
|
796
|
+
```bash
|
|
797
|
+
curl -X PUT /api/sdk/builds/build-uuid/status \
|
|
798
|
+
-H "Authorization: Bearer your-api-token" \
|
|
799
|
+
-H "User-Agent: Vizzly-CLI/1.0.0" \
|
|
800
|
+
-H "Content-Type: application/json" \
|
|
801
|
+
-d '{"status": "completed"}'
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
## Parallel Build Support
|
|
805
|
+
|
|
806
|
+
Vizzly supports parallel test execution where multiple test runners (shards) contribute screenshots to a single shared build. This is particularly useful for CI/CD workflows with parallel jobs.
|
|
807
|
+
|
|
808
|
+
### How Parallel Builds Work
|
|
809
|
+
|
|
810
|
+
1. **Single Build**: All shards share one build using a common `parallel_id`
|
|
811
|
+
2. **First Shard Creates**: The first shard to call the API creates the build
|
|
812
|
+
3. **Subsequent Shards Reuse**: Other shards find and reuse the existing build
|
|
813
|
+
4. **Automatic Reopening**: Existing build reopening logic handles concurrent uploads
|
|
814
|
+
5. **Shard Completion**: Individual shards can update status to `"failed"` but NOT `"completed"`
|
|
815
|
+
6. **Manual Finalization**: ONLY the finalize endpoint can complete parallel builds
|
|
816
|
+
|
|
817
|
+
### New Endpoints for Parallel Builds
|
|
818
|
+
|
|
819
|
+
#### Finalize Parallel Build
|
|
820
|
+
|
|
821
|
+
Marks a parallel build as completed after all shards finish.
|
|
822
|
+
|
|
823
|
+
**Endpoint:** `POST /api/sdk/parallel/:parallelId/finalize`
|
|
824
|
+
|
|
825
|
+
**Response (200):**
|
|
826
|
+
|
|
827
|
+
```json
|
|
828
|
+
{
|
|
829
|
+
"message": "Parallel build finalized successfully",
|
|
830
|
+
"build": {
|
|
831
|
+
"id": "build-uuid",
|
|
832
|
+
"status": "completed",
|
|
833
|
+
"parallel_id": "workflow-12345-attempt-1"
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
### Usage Example (GitHub Actions)
|
|
839
|
+
|
|
840
|
+
```yaml
|
|
841
|
+
jobs:
|
|
842
|
+
e2e-tests:
|
|
843
|
+
strategy:
|
|
844
|
+
matrix:
|
|
845
|
+
shard: [1/4, 2/4, 3/4, 4/4]
|
|
846
|
+
steps:
|
|
847
|
+
- name: Run tests with Vizzly
|
|
848
|
+
run: |
|
|
849
|
+
npx vizzly run "npm test -- --shard=${{ matrix.shard }}" \
|
|
850
|
+
--parallel-id="${{ github.run_id }}-${{ github.run_attempt }}"
|
|
851
|
+
|
|
852
|
+
finalize-e2e:
|
|
853
|
+
needs: e2e-tests
|
|
854
|
+
runs-on: ubuntu-latest
|
|
855
|
+
if: always() && needs.e2e-tests.result == 'success'
|
|
856
|
+
steps:
|
|
857
|
+
- name: Finalize parallel build
|
|
858
|
+
run: |
|
|
859
|
+
curl -X POST "https://api.vizzly.co/api/sdk/parallel/${{ github.run_id }}-${{ github.run_attempt }}/finalize" \
|
|
860
|
+
-H "Authorization: Bearer ${{ secrets.VIZZLY_TOKEN }}" \
|
|
861
|
+
-H "User-Agent: GitHub-Actions/1.0"
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
### Best Practices
|
|
865
|
+
|
|
866
|
+
- Use GitHub workflow run ID + attempt number for `parallel_id` to ensure uniqueness
|
|
867
|
+
- Always call the finalize endpoint after all shards complete
|
|
868
|
+
- The finalize job should depend on all shard jobs completing successfully
|
|
869
|
+
|
|
870
|
+
## Hotspot Analysis (TDD Mode)
|
|
871
|
+
|
|
872
|
+
Hotspot endpoints provide historical data about regions that frequently change across builds (timestamps, dynamic IDs, ads, etc.). This data helps the CLI's TDD mode intelligently filter out known dynamic content.
|
|
873
|
+
|
|
874
|
+
### Get Screenshot Hotspots
|
|
875
|
+
|
|
876
|
+
Retrieves hotspot analysis for a specific screenshot name.
|
|
877
|
+
|
|
878
|
+
**Endpoint:** `GET /api/sdk/screenshots/:screenshotName/hotspots`
|
|
879
|
+
|
|
880
|
+
**Query Parameters:**
|
|
881
|
+
|
|
882
|
+
- `windowSize` (optional, default: 20, max: 50): Number of historical builds to analyze
|
|
883
|
+
|
|
884
|
+
**Response (200):**
|
|
885
|
+
|
|
886
|
+
```json
|
|
887
|
+
{
|
|
888
|
+
"screenshot_name": "homepage-desktop-chrome",
|
|
889
|
+
"hotspot_analysis": {
|
|
890
|
+
"regions": [
|
|
891
|
+
{
|
|
892
|
+
"y1": 100,
|
|
893
|
+
"y2": 150,
|
|
894
|
+
"height": 50,
|
|
895
|
+
"frequency": 0.85,
|
|
896
|
+
"lineCount": 50
|
|
897
|
+
}
|
|
898
|
+
],
|
|
899
|
+
"total_builds_analyzed": 15,
|
|
900
|
+
"confidence": "high",
|
|
901
|
+
"confidence_score": 85,
|
|
902
|
+
"confidence_factors": {
|
|
903
|
+
"build_count": 15,
|
|
904
|
+
"data_source": "approved",
|
|
905
|
+
"frequency_consistency": 0.9
|
|
906
|
+
},
|
|
907
|
+
"data_source": "approved",
|
|
908
|
+
"frequency_threshold": 0.7
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
**Confidence Values:**
|
|
914
|
+
|
|
915
|
+
- `"high"`: Strong historical data, reliable hotspot detection
|
|
916
|
+
- `"medium"`: Moderate data, reasonably reliable
|
|
917
|
+
- `"low"`: Limited data, use with caution
|
|
918
|
+
- `"insufficient_data"`: Not enough historical builds to determine hotspots
|
|
919
|
+
|
|
920
|
+
**Data Source Values:**
|
|
921
|
+
|
|
922
|
+
- `"approved"`: Hotspots derived from approved builds only (highest quality)
|
|
923
|
+
- `"mixed"`: Includes both approved and unapproved builds
|
|
924
|
+
- `"none"`: No historical data available
|
|
925
|
+
|
|
926
|
+
### Batch Get Screenshot Hotspots
|
|
927
|
+
|
|
928
|
+
Retrieves hotspot analysis for multiple screenshots in a single request. More efficient for TDD baseline downloads.
|
|
929
|
+
|
|
930
|
+
**Endpoint:** `POST /api/sdk/screenshots/hotspots`
|
|
931
|
+
|
|
932
|
+
**Request Body:**
|
|
933
|
+
|
|
934
|
+
```json
|
|
935
|
+
{
|
|
936
|
+
"screenshot_names": [
|
|
937
|
+
"homepage-desktop-chrome",
|
|
938
|
+
"dashboard-mobile-safari",
|
|
939
|
+
"settings-tablet-firefox"
|
|
940
|
+
],
|
|
941
|
+
"windowSize": 20
|
|
942
|
+
}
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
**Constraints:**
|
|
946
|
+
|
|
947
|
+
- Maximum 100 screenshot names per request
|
|
948
|
+
- Screenshot names must be valid (no path traversal, max 255 characters)
|
|
949
|
+
|
|
950
|
+
**Response (200):**
|
|
951
|
+
|
|
952
|
+
```json
|
|
953
|
+
{
|
|
954
|
+
"hotspots": {
|
|
955
|
+
"homepage-desktop-chrome": {
|
|
956
|
+
"regions": [
|
|
957
|
+
{
|
|
958
|
+
"y1": 100,
|
|
959
|
+
"y2": 150,
|
|
960
|
+
"height": 50,
|
|
961
|
+
"frequency": 0.85,
|
|
962
|
+
"lineCount": 50
|
|
963
|
+
}
|
|
964
|
+
],
|
|
965
|
+
"total_builds_analyzed": 15,
|
|
966
|
+
"confidence": "high",
|
|
967
|
+
"confidence_score": 85,
|
|
968
|
+
"data_source": "approved"
|
|
969
|
+
},
|
|
970
|
+
"dashboard-mobile-safari": {
|
|
971
|
+
"regions": [],
|
|
972
|
+
"total_builds_analyzed": 8,
|
|
973
|
+
"confidence": "medium",
|
|
974
|
+
"confidence_score": 60,
|
|
975
|
+
"data_source": "mixed"
|
|
976
|
+
}
|
|
977
|
+
},
|
|
978
|
+
"summary": {
|
|
979
|
+
"requested": 3,
|
|
980
|
+
"returned": 2
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
**Notes:**
|
|
986
|
+
|
|
987
|
+
- Only screenshots with `total_builds_analyzed > 0` are included in the response
|
|
988
|
+
- Screenshots without historical data are omitted from the `hotspots` object
|
|
989
|
+
- The `summary` shows how many screenshots were requested vs returned
|
|
990
|
+
|
|
991
|
+
### Usage Example (CLI TDD Mode)
|
|
992
|
+
|
|
993
|
+
```bash
|
|
994
|
+
# Fetch hotspots for baseline download
|
|
995
|
+
curl -X POST /api/sdk/screenshots/hotspots \
|
|
996
|
+
-H "Authorization: Bearer your-api-token" \
|
|
997
|
+
-H "User-Agent: Vizzly-CLI/1.0.0" \
|
|
998
|
+
-H "Content-Type: application/json" \
|
|
999
|
+
-d '{"screenshot_names": ["homepage", "dashboard", "settings"]}'
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
The CLI uses this data to:
|
|
1003
|
+
|
|
1004
|
+
1. Identify regions that historically change frequently
|
|
1005
|
+
2. Apply intelligent filtering during local comparisons
|
|
1006
|
+
3. Reduce false positives from dynamic content like timestamps or ads
|
|
1007
|
+
|
|
1008
|
+
## Implementation Notes
|
|
1009
|
+
|
|
1010
|
+
- All image data should be base64 encoded when sent via JSON
|
|
1011
|
+
- File uploads use standard multipart/form-data encoding
|
|
1012
|
+
- SHA256 hashes are used for deduplication and efficient uploads
|
|
1013
|
+
- Build processing happens asynchronously after screenshot upload
|
|
1014
|
+
- Screenshots are automatically processed for comparisons when uploaded
|
|
1015
|
+
- Builds can be reopened if new screenshots arrive (useful for parallel test execution)
|
|
1016
|
+
- Storage quota is checked before accepting uploads
|
|
1017
|
+
- **Parallel builds**: Multiple shards share a single build using `parallel_id` for efficient parallel testing
|
|
1018
|
+
- **Build finalization**: Use the finalize endpoint to complete parallel builds after all shards finish
|