@vizzly-testing/cli 0.34.0 → 0.35.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/README.md +67 -7
- package/dist/api/core.js +18 -4
- package/dist/api/endpoints.js +38 -15
- package/dist/api/index.js +1 -1
- package/dist/cli.js +270 -194
- package/dist/client/index.js +62 -21
- package/dist/commands/api.js +9 -3
- package/dist/commands/preview.js +30 -26
- package/dist/commands/project.js +1 -1
- package/dist/commands/run.js +25 -10
- package/dist/commands/status.js +14 -1
- package/dist/commands/tdd-daemon-process.js +5 -1
- package/dist/commands/tdd-daemon.js +217 -139
- package/dist/commands/tdd.js +26 -5
- package/dist/commands/upload.js +31 -13
- package/dist/reporter/reporter-bundle.iife.js +6 -6
- package/dist/sdk/index.js +23 -9
- package/dist/server/handlers/api-handler.js +3 -2
- package/dist/server/handlers/tdd-handler.js +5 -1
- package/dist/server/routers/screenshot.js +11 -4
- package/dist/services/build-manager.js +6 -1
- package/dist/tdd/tdd-service.js +6 -2
- package/dist/test-runner/core.js +8 -3
- package/dist/test-runner/operations.js +19 -3
- package/dist/types/client.d.ts +32 -5
- package/dist/types/index.d.ts +33 -5
- package/dist/uploader/core.js +41 -9
- package/dist/uploader/operations.js +19 -17
- package/dist/utils/config-loader.js +13 -6
- package/dist/utils/context.js +46 -76
- package/dist/utils/environment-config.js +20 -0
- package/dist/utils/output.js +8 -5
- package/dist/utils/screenshot-options.js +60 -10
- package/package.json +1 -1
- package/skills/vizzly/SKILL.md +12 -4
- package/skills/vizzly/references/cli-context.md +8 -2
- package/skills/vizzly/references/setup-ci.md +3 -1
|
@@ -2,7 +2,7 @@ import { resolve } from 'node:path';
|
|
|
2
2
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
3
3
|
import { CONFIG_DEFAULTS, deepMerge } from '../config/core.js';
|
|
4
4
|
import { validateVizzlyConfigWithDefaults } from './config-schema.js';
|
|
5
|
-
import { getApiToken, getApiUrl, getBuildName, getParallelId } from './environment-config.js';
|
|
5
|
+
import { getApiToken, getApiUrl, getBuildName, getMinClusterSize, getParallelId, getThreshold } from './environment-config.js';
|
|
6
6
|
import { getAccessToken } from './global-config.js';
|
|
7
7
|
import * as output from './output.js';
|
|
8
8
|
import { getActiveProjectLink } from './project-link-store.js';
|
|
@@ -26,10 +26,12 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
26
26
|
mergeConfig(config, validatedFileConfig);
|
|
27
27
|
|
|
28
28
|
// 3. Override with environment variables (higher priority than fallbacks)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
let envApiKey = getApiToken();
|
|
30
|
+
let envApiUrl = getApiUrl();
|
|
31
|
+
let envBuildName = getBuildName();
|
|
32
|
+
let envParallelId = getParallelId();
|
|
33
|
+
let envThreshold = getThreshold();
|
|
34
|
+
let envMinClusterSize = getMinClusterSize();
|
|
33
35
|
if (envApiKey) {
|
|
34
36
|
config.apiKey = envApiKey;
|
|
35
37
|
output.debug('config', 'using token from environment');
|
|
@@ -40,6 +42,10 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
40
42
|
output.debug('config', 'using build name from environment');
|
|
41
43
|
}
|
|
42
44
|
if (envParallelId) config.parallelId = envParallelId;
|
|
45
|
+
if (envThreshold !== undefined) config.comparison.threshold = envThreshold;
|
|
46
|
+
if (envMinClusterSize !== undefined) {
|
|
47
|
+
config.comparison.minClusterSize = envMinClusterSize;
|
|
48
|
+
}
|
|
43
49
|
|
|
44
50
|
// 4. Apply CLI overrides (highest priority)
|
|
45
51
|
if (cliOverrides.token) {
|
|
@@ -76,6 +82,7 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
76
82
|
config.userToken = userToken;
|
|
77
83
|
output.debug('config', 'using user login for user-authenticated commands');
|
|
78
84
|
}
|
|
85
|
+
config._configPath = result?.filepath || null;
|
|
79
86
|
return config;
|
|
80
87
|
}
|
|
81
88
|
|
|
@@ -109,7 +116,7 @@ function applyCLIOverrides(config, cliOverrides = {}) {
|
|
|
109
116
|
}
|
|
110
117
|
|
|
111
118
|
// Comparison overrides
|
|
112
|
-
if (cliOverrides.threshold !== undefined) config.comparison.threshold = cliOverrides.threshold;
|
|
119
|
+
if (cliOverrides.threshold !== undefined) config.comparison.threshold = Number(cliOverrides.threshold);
|
|
113
120
|
if (cliOverrides.minClusterSize !== undefined) {
|
|
114
121
|
config.comparison.minClusterSize = Number(cliOverrides.minClusterSize);
|
|
115
122
|
}
|
package/dist/utils/context.js
CHANGED
|
@@ -11,6 +11,37 @@
|
|
|
11
11
|
import { existsSync, readFileSync } from 'node:fs';
|
|
12
12
|
import { homedir } from 'node:os';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
+
function getGlobalConfigPath() {
|
|
15
|
+
return join(process.env.VIZZLY_HOME || join(homedir(), '.vizzly'), 'config.json');
|
|
16
|
+
}
|
|
17
|
+
function readJsonFile(path) {
|
|
18
|
+
try {
|
|
19
|
+
if (existsSync(path)) {
|
|
20
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
// Optional context should never block the command that is rendering it.
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function readGlobalConfig() {
|
|
28
|
+
return readJsonFile(getGlobalConfigPath()) || {};
|
|
29
|
+
}
|
|
30
|
+
function readBaselineInfo(cwd) {
|
|
31
|
+
let path = join(cwd, '.vizzly', 'baselines');
|
|
32
|
+
let metadata = readJsonFile(join(path, 'metadata.json'));
|
|
33
|
+
return {
|
|
34
|
+
count: metadata?.screenshots?.length || 0,
|
|
35
|
+
path: metadata ? path : null
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function readServerInfo(cwd) {
|
|
39
|
+
let serverInfo = readJsonFile(join(cwd, '.vizzly', 'server.json'));
|
|
40
|
+
return {
|
|
41
|
+
running: !!serverInfo,
|
|
42
|
+
port: serverInfo ? serverInfo.port : null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
14
45
|
|
|
15
46
|
/**
|
|
16
47
|
* Get dynamic context about the current Vizzly state
|
|
@@ -22,46 +53,12 @@ export function getContext() {
|
|
|
22
53
|
let items = [];
|
|
23
54
|
try {
|
|
24
55
|
let cwd = process.cwd();
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
// Load global config once
|
|
28
|
-
let globalConfig = {};
|
|
29
|
-
try {
|
|
30
|
-
if (existsSync(globalConfigPath)) {
|
|
31
|
-
globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf8'));
|
|
32
|
-
}
|
|
33
|
-
} catch {
|
|
34
|
-
// Ignore
|
|
35
|
-
}
|
|
56
|
+
let globalConfig = readGlobalConfig();
|
|
36
57
|
|
|
37
58
|
// Check for vizzly.config.js (project config)
|
|
38
59
|
let hasProjectConfig = existsSync(join(cwd, 'vizzly.config.js'));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let baselineCount = 0;
|
|
42
|
-
try {
|
|
43
|
-
let metaPath = join(cwd, '.vizzly', 'baselines', 'metadata.json');
|
|
44
|
-
if (existsSync(metaPath)) {
|
|
45
|
-
let meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
46
|
-
baselineCount = meta.screenshots?.length || 0;
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
// Ignore
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Check for TDD server running
|
|
53
|
-
let serverRunning = false;
|
|
54
|
-
let serverPort = null;
|
|
55
|
-
try {
|
|
56
|
-
let serverFile = join(cwd, '.vizzly', 'server.json');
|
|
57
|
-
if (existsSync(serverFile)) {
|
|
58
|
-
let serverInfo = JSON.parse(readFileSync(serverFile, 'utf8'));
|
|
59
|
-
serverPort = serverInfo.port;
|
|
60
|
-
serverRunning = true;
|
|
61
|
-
}
|
|
62
|
-
} catch {
|
|
63
|
-
// Ignore
|
|
64
|
-
}
|
|
60
|
+
let baselineInfo = readBaselineInfo(cwd);
|
|
61
|
+
let serverInfo = readServerInfo(cwd);
|
|
65
62
|
|
|
66
63
|
// Check for OAuth login (from vizzly login)
|
|
67
64
|
let isLoggedIn = !!globalConfig.auth?.accessToken;
|
|
@@ -71,11 +68,11 @@ export function getContext() {
|
|
|
71
68
|
let hasEnvToken = !!process.env.VIZZLY_TOKEN;
|
|
72
69
|
|
|
73
70
|
// Build context items - prioritize most useful info
|
|
74
|
-
if (
|
|
71
|
+
if (serverInfo.running) {
|
|
75
72
|
items.push({
|
|
76
73
|
type: 'success',
|
|
77
74
|
label: 'TDD Server',
|
|
78
|
-
value: `running on :${
|
|
75
|
+
value: `running on :${serverInfo.port}`
|
|
79
76
|
});
|
|
80
77
|
}
|
|
81
78
|
if (isLoggedIn && userName) {
|
|
@@ -97,14 +94,14 @@ export function getContext() {
|
|
|
97
94
|
value: 'run vizzly login'
|
|
98
95
|
});
|
|
99
96
|
}
|
|
100
|
-
if (
|
|
97
|
+
if (baselineInfo.count > 0) {
|
|
101
98
|
items.push({
|
|
102
99
|
type: 'success',
|
|
103
100
|
label: 'Baselines',
|
|
104
|
-
value: `${
|
|
101
|
+
value: `${baselineInfo.count} screenshots`
|
|
105
102
|
});
|
|
106
103
|
}
|
|
107
|
-
if (!hasProjectConfig && !
|
|
104
|
+
if (!hasProjectConfig && !serverInfo.running && baselineInfo.count === 0) {
|
|
108
105
|
// Only show "no config" hint if there's nothing else useful
|
|
109
106
|
items.push({
|
|
110
107
|
type: 'info',
|
|
@@ -126,7 +123,6 @@ export function getContext() {
|
|
|
126
123
|
*/
|
|
127
124
|
export function getDetailedContext() {
|
|
128
125
|
let cwd = process.cwd();
|
|
129
|
-
let globalConfigPath = join(process.env.VIZZLY_HOME || join(homedir(), '.vizzly'), 'config.json');
|
|
130
126
|
let context = {
|
|
131
127
|
tddServer: {
|
|
132
128
|
running: false,
|
|
@@ -146,42 +142,16 @@ export function getDetailedContext() {
|
|
|
146
142
|
}
|
|
147
143
|
};
|
|
148
144
|
try {
|
|
149
|
-
|
|
150
|
-
let globalConfig = {};
|
|
151
|
-
try {
|
|
152
|
-
if (existsSync(globalConfigPath)) {
|
|
153
|
-
globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf8'));
|
|
154
|
-
}
|
|
155
|
-
} catch {
|
|
156
|
-
// Ignore
|
|
157
|
-
}
|
|
145
|
+
let globalConfig = readGlobalConfig();
|
|
158
146
|
|
|
159
147
|
// Check for vizzly.config.js
|
|
160
148
|
context.project.hasConfig = existsSync(join(cwd, 'vizzly.config.js'));
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
context.baselines.count = meta.screenshots?.length || 0;
|
|
168
|
-
context.baselines.path = join(cwd, '.vizzly', 'baselines');
|
|
169
|
-
}
|
|
170
|
-
} catch {
|
|
171
|
-
// Ignore
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Check for TDD server
|
|
175
|
-
try {
|
|
176
|
-
let serverFile = join(cwd, '.vizzly', 'server.json');
|
|
177
|
-
if (existsSync(serverFile)) {
|
|
178
|
-
let serverInfo = JSON.parse(readFileSync(serverFile, 'utf8'));
|
|
179
|
-
context.tddServer.running = true;
|
|
180
|
-
context.tddServer.port = serverInfo.port;
|
|
181
|
-
}
|
|
182
|
-
} catch {
|
|
183
|
-
// Ignore
|
|
184
|
-
}
|
|
149
|
+
let baselineInfo = readBaselineInfo(cwd);
|
|
150
|
+
context.baselines.count = baselineInfo.count;
|
|
151
|
+
context.baselines.path = baselineInfo.path;
|
|
152
|
+
let serverInfo = readServerInfo(cwd);
|
|
153
|
+
context.tddServer.running = serverInfo.running;
|
|
154
|
+
context.tddServer.port = serverInfo.port;
|
|
185
155
|
|
|
186
156
|
// Check auth status
|
|
187
157
|
context.auth.loggedIn = !!globalConfig.auth?.accessToken;
|
|
@@ -85,6 +85,24 @@ export function getBuildName() {
|
|
|
85
85
|
return process.env.VIZZLY_BUILD_NAME;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Get comparison threshold from environment
|
|
90
|
+
* @returns {number|undefined} Threshold value when set
|
|
91
|
+
*/
|
|
92
|
+
export function getThreshold() {
|
|
93
|
+
if (process.env.VIZZLY_THRESHOLD === undefined) return undefined;
|
|
94
|
+
return Number(process.env.VIZZLY_THRESHOLD);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get minimum changed cluster size from environment
|
|
99
|
+
* @returns {number|undefined} Minimum cluster size when set
|
|
100
|
+
*/
|
|
101
|
+
export function getMinClusterSize() {
|
|
102
|
+
if (process.env.VIZZLY_MIN_CLUSTER_SIZE === undefined) return undefined;
|
|
103
|
+
return Number(process.env.VIZZLY_MIN_CLUSTER_SIZE);
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
/**
|
|
89
107
|
* Check if TDD mode is enabled
|
|
90
108
|
* @returns {boolean} Whether TDD mode is enabled
|
|
@@ -117,6 +135,8 @@ export function getAllEnvironmentConfig() {
|
|
|
117
135
|
buildId: getBuildId(),
|
|
118
136
|
buildName: getBuildName(),
|
|
119
137
|
parallelId: getParallelId(),
|
|
138
|
+
threshold: getThreshold(),
|
|
139
|
+
minClusterSize: getMinClusterSize(),
|
|
120
140
|
tddMode: isTddMode()
|
|
121
141
|
};
|
|
122
142
|
}
|
package/dist/utils/output.js
CHANGED
|
@@ -232,6 +232,9 @@ export function getLogLevel() {
|
|
|
232
232
|
export function isVerbose() {
|
|
233
233
|
return config.logLevel === 'debug';
|
|
234
234
|
}
|
|
235
|
+
export function isJson() {
|
|
236
|
+
return config.json;
|
|
237
|
+
}
|
|
235
238
|
|
|
236
239
|
/**
|
|
237
240
|
* Show command header with distinctive branding
|
|
@@ -282,7 +285,7 @@ export function success(message, data = {}) {
|
|
|
282
285
|
});
|
|
283
286
|
if (config.silent) return;
|
|
284
287
|
if (config.json) {
|
|
285
|
-
console.
|
|
288
|
+
console.error(JSON.stringify({
|
|
286
289
|
status: 'success',
|
|
287
290
|
message,
|
|
288
291
|
...data
|
|
@@ -301,7 +304,7 @@ export function result(message) {
|
|
|
301
304
|
if (config.silent) return;
|
|
302
305
|
const elapsed = getElapsedTime();
|
|
303
306
|
if (config.json) {
|
|
304
|
-
console.
|
|
307
|
+
console.error(JSON.stringify({
|
|
305
308
|
status: 'complete',
|
|
306
309
|
message,
|
|
307
310
|
elapsed
|
|
@@ -319,7 +322,7 @@ export function info(message, data = {}) {
|
|
|
319
322
|
writeLog('info', message, data);
|
|
320
323
|
if (!shouldLog('info')) return;
|
|
321
324
|
if (config.json) {
|
|
322
|
-
console.
|
|
325
|
+
console.error(JSON.stringify({
|
|
323
326
|
status: 'info',
|
|
324
327
|
message,
|
|
325
328
|
...data
|
|
@@ -513,7 +516,7 @@ export function stopSpinner() {
|
|
|
513
516
|
export function progress(message, current = 0, total = 0) {
|
|
514
517
|
if (config.silent) return;
|
|
515
518
|
if (config.json) {
|
|
516
|
-
console.
|
|
519
|
+
console.error(JSON.stringify({
|
|
517
520
|
status: 'progress',
|
|
518
521
|
message,
|
|
519
522
|
progress: {
|
|
@@ -941,7 +944,7 @@ export function complete(message, options = {}) {
|
|
|
941
944
|
} = options;
|
|
942
945
|
let detailStr = detail ? ` ${colors.brand.textMuted(detail)}` : '';
|
|
943
946
|
if (config.json) {
|
|
944
|
-
console.
|
|
947
|
+
console.error(JSON.stringify({
|
|
945
948
|
status: 'complete',
|
|
946
949
|
message,
|
|
947
950
|
detail
|
|
@@ -1,21 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshot option names that are part of the SDK/config contract, not the
|
|
3
|
+
* user's arbitrary metadata bag.
|
|
4
|
+
*/
|
|
5
|
+
export let RESERVED_PROPERTY_OPTIONS = Object.freeze({
|
|
6
|
+
threshold: {
|
|
7
|
+
message: 'Move "threshold" out of properties; properties is only for user metadata.'
|
|
8
|
+
},
|
|
9
|
+
minClusterSize: {
|
|
10
|
+
message: 'Move "minClusterSize" out of properties; properties is only for user metadata.'
|
|
11
|
+
},
|
|
12
|
+
fullPage: {
|
|
13
|
+
message: 'Move "fullPage" out of properties; properties is only for user metadata.'
|
|
14
|
+
},
|
|
15
|
+
buildId: {
|
|
16
|
+
message: 'Move "buildId" out of properties; properties is only for user metadata.'
|
|
17
|
+
},
|
|
18
|
+
requestTimeout: {
|
|
19
|
+
message: 'Move "requestTimeout" out of properties; properties is only for user metadata.'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
function createReservedPropertyWarning(option) {
|
|
23
|
+
return {
|
|
24
|
+
code: 'reserved-property-option',
|
|
25
|
+
option,
|
|
26
|
+
message: RESERVED_PROPERTY_OPTIONS[option].message
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
1
30
|
/**
|
|
2
31
|
* Normalize screenshot SDK options into the properties payload consumed by the
|
|
3
32
|
* local TDD server and cloud-compatible comparison path.
|
|
4
33
|
*/
|
|
5
|
-
export function
|
|
34
|
+
export function normalizeScreenshotOptions(options = {}) {
|
|
6
35
|
let {
|
|
7
|
-
buildId
|
|
36
|
+
buildId,
|
|
8
37
|
properties = {},
|
|
9
|
-
requestTimeout
|
|
38
|
+
requestTimeout,
|
|
10
39
|
threshold,
|
|
11
40
|
minClusterSize,
|
|
12
|
-
fullPage
|
|
13
|
-
...topLevelProperties
|
|
41
|
+
fullPage
|
|
14
42
|
} = options;
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
43
|
+
let warnings = [];
|
|
44
|
+
let normalizedProperties = {};
|
|
45
|
+
for (let [key, value] of Object.entries(properties || {})) {
|
|
46
|
+
if (RESERVED_PROPERTY_OPTIONS[key]) {
|
|
47
|
+
warnings.push(createReservedPropertyWarning(key));
|
|
48
|
+
if (key === 'threshold' && threshold === undefined) threshold = value;
|
|
49
|
+
if (key === 'minClusterSize' && minClusterSize === undefined) {
|
|
50
|
+
minClusterSize = value;
|
|
51
|
+
}
|
|
52
|
+
if (key === 'fullPage' && fullPage === undefined) fullPage = value;
|
|
53
|
+
if (key === 'buildId' && buildId === undefined) buildId = value;
|
|
54
|
+
if (key === 'requestTimeout' && requestTimeout === undefined) {
|
|
55
|
+
requestTimeout = value;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
normalizedProperties[key] = value;
|
|
60
|
+
}
|
|
19
61
|
if (threshold !== undefined) {
|
|
20
62
|
normalizedProperties.threshold = threshold;
|
|
21
63
|
}
|
|
@@ -25,5 +67,13 @@ export function createScreenshotProperties(options = {}) {
|
|
|
25
67
|
if (fullPage !== undefined) {
|
|
26
68
|
normalizedProperties.fullPage = fullPage;
|
|
27
69
|
}
|
|
28
|
-
return
|
|
70
|
+
return {
|
|
71
|
+
buildId,
|
|
72
|
+
requestTimeout,
|
|
73
|
+
properties: normalizedProperties,
|
|
74
|
+
warnings
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function createScreenshotProperties(options = {}) {
|
|
78
|
+
return normalizeScreenshotOptions(options).properties;
|
|
29
79
|
}
|
package/package.json
CHANGED
package/skills/vizzly/SKILL.md
CHANGED
|
@@ -23,16 +23,24 @@ If a task names a screen, component, or screenshot, inspect that screenshot's hi
|
|
|
23
23
|
vizzly context screenshot "<screenshot-name>" --source local --json
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
When you make or verify UI changes,
|
|
26
|
+
When you make or verify UI changes, use the local TDD server as the active feedback loop:
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
vizzly tdd
|
|
29
|
+
vizzly tdd start &
|
|
30
|
+
npm run test:e2e -- tests/e2e/path.spec.js
|
|
31
|
+
vizzly context build current --source local --agent --json
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
The user may choose whether the TDD server runs in the foreground or background. Once it is
|
|
35
|
+
running, run tests normally from the repo. The SDK discovers `.vizzly/server.json`, posts
|
|
36
|
+
screenshots to the active TDD server, and refreshes `.vizzly/current`, `.vizzly/diffs`, report
|
|
37
|
+
data, and local context.
|
|
38
|
+
|
|
39
|
+
Use `vizzly tdd run` for true one-off runs where you want the CLI to own starting the visual
|
|
40
|
+
session and running the command:
|
|
33
41
|
|
|
34
42
|
```bash
|
|
35
|
-
vizzly
|
|
43
|
+
vizzly tdd run "<test command>" --no-open
|
|
36
44
|
```
|
|
37
45
|
|
|
38
46
|
For specific evidence, drill in:
|
|
@@ -5,13 +5,19 @@ Use these commands to gather Vizzly evidence before making UI assumptions.
|
|
|
5
5
|
## Local TDD
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
vizzly tdd start
|
|
8
|
+
vizzly tdd start --open
|
|
9
|
+
vizzly tdd status
|
|
10
|
+
vizzly tdd stop
|
|
9
11
|
vizzly tdd run "<test command>" --no-open
|
|
10
12
|
vizzly context build current --source local --agent
|
|
11
13
|
vizzly context build current --source local --agent --json
|
|
12
14
|
```
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
If `tdd start` prints a non-default port, pass that same port to
|
|
17
|
+
`tdd status --port <port>` and `tdd stop --port <port>`.
|
|
18
|
+
|
|
19
|
+
Local context reads `.vizzly` state and does not require cloud auth. After a
|
|
20
|
+
one-shot `tdd run`, prefer the printed `contextCommand` before editing UI.
|
|
15
21
|
|
|
16
22
|
## Cloud Builds
|
|
17
23
|
|
|
@@ -6,11 +6,13 @@ Use Vizzly locally for fast feedback and in CI for shared review.
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
vizzly init
|
|
9
|
-
vizzly tdd start
|
|
9
|
+
vizzly tdd start --open
|
|
10
10
|
vizzly tdd run "<test command>" --no-open
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
The local SDK discovers `.vizzly/server.json` and sends screenshots to the TDD server when it is running.
|
|
14
|
+
For one-off checks, `vizzly tdd run` writes local review data under `.vizzly/`
|
|
15
|
+
and prints a context command for follow-up inspection.
|
|
14
16
|
|
|
15
17
|
## Cloud Setup
|
|
16
18
|
|