@vizzly-testing/cli 0.35.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.
@@ -269,25 +269,10 @@ export async function previewCommand(path, options = {}, globalOptions = {}, dep
269
269
  color: !globalOptions.noColor
270
270
  });
271
271
  try {
272
- // Load configuration
273
- let allOptions = {
274
- ...globalOptions,
275
- ...options
276
- };
277
- let config = await loadConfig(globalOptions.config, allOptions);
278
-
279
- // Validate API token (skip for dry-run)
280
- if (!options.dryRun && !config.apiKey) {
281
- output.error('API token required. Use --token or set VIZZLY_TOKEN environment variable');
282
- exit(1);
283
- return {
284
- success: false,
285
- reason: 'no-api-key'
286
- };
287
- }
288
272
  let uploadPath = options.base ? resolve(path, options.base) : path;
289
273
 
290
- // Validate path exists and is a directory
274
+ // Validate path exists and is a directory before auth so local input
275
+ // mistakes are visible without requiring credentials.
291
276
  if (!existsSync(uploadPath)) {
292
277
  output.error(`Path does not exist: ${uploadPath}`);
293
278
  exit(1);
@@ -306,6 +291,23 @@ export async function previewCommand(path, options = {}, globalOptions = {}, dep
306
291
  };
307
292
  }
308
293
 
294
+ // Load configuration
295
+ let allOptions = {
296
+ ...globalOptions,
297
+ ...options
298
+ };
299
+ let config = await loadConfig(globalOptions.config, allOptions);
300
+
301
+ // Validate API token (skip for dry-run)
302
+ if (!options.dryRun && !config.apiKey) {
303
+ output.error('API token required. Use --token or set VIZZLY_TOKEN environment variable');
304
+ exit(1);
305
+ return {
306
+ success: false,
307
+ reason: 'no-api-key'
308
+ };
309
+ }
310
+
309
311
  // Resolve build ID
310
312
  let buildId = options.build;
311
313
  let buildSource = 'flag';
@@ -246,7 +246,8 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
246
246
  wait: config.wait || options.wait || false,
247
247
  uploadAll: options.uploadAll || false,
248
248
  pullRequestNumber,
249
- parallelId: config.parallelId
249
+ parallelId: config.parallelId,
250
+ json: globalOptions.json
250
251
  };
251
252
 
252
253
  // Start test run
@@ -336,10 +337,12 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
336
337
  result
337
338
  };
338
339
  }
339
- output.complete('Test run completed');
340
+ if (!globalOptions.json) {
341
+ output.complete('Test run completed');
342
+ }
340
343
 
341
344
  // Show Vizzly summary with link to results
342
- if (result.buildId) {
345
+ if (result.buildId && !globalOptions.json) {
343
346
  output.blank();
344
347
  let colors = output.getColors();
345
348
  output.print(` ${colors.brand.textTertiary('Screenshots')} ${colors.white(result.screenshotsCaptured)}`);
@@ -386,7 +389,7 @@ export async function runCommand(testCommand, options = {}, globalOptions = {},
386
389
  });
387
390
  output.cleanup();
388
391
  } else {
389
- output.error('Test run failed');
392
+ output.error('Test run failed', error);
390
393
  }
391
394
  return {
392
395
  success: false,
@@ -36,6 +36,19 @@ function createStatusClient({
36
36
  command: 'status'
37
37
  });
38
38
  }
39
+ function isMissingPreviewError(error) {
40
+ return error.context?.status === 404;
41
+ }
42
+ async function fetchOptionalPreviewInfo(getPreviewInfo, client, buildId) {
43
+ try {
44
+ return await getPreviewInfo(client, buildId);
45
+ } catch (error) {
46
+ if (isMissingPreviewError(error)) {
47
+ return null;
48
+ }
49
+ throw error;
50
+ }
51
+ }
39
52
  export function normalizeBuildStatus(buildStatus) {
40
53
  return buildStatus.build || buildStatus;
41
54
  }
@@ -251,7 +264,7 @@ export async function statusCommand(buildId, options = {}, globalOptions = {}, d
251
264
  let buildStatus = await getBuild(client, buildId);
252
265
 
253
266
  // Also fetch preview info (if exists)
254
- let previewInfo = await getPreviewInfo(client, buildId);
267
+ let previewInfo = await fetchOptionalPreviewInfo(getPreviewInfo, client, buildId);
255
268
  output.stopSpinner();
256
269
 
257
270
  // Extract build data from API response
@@ -18,7 +18,7 @@ function isProcessRunning(pid) {
18
18
  return false;
19
19
  }
20
20
  }
21
- function parsePositiveInteger(value) {
21
+ export function parsePositiveInteger(value) {
22
22
  let text = String(value).trim();
23
23
  if (!/^\d+$/.test(text)) {
24
24
  return null;
@@ -71,12 +71,16 @@ export async function resolveDaemonPid({
71
71
  pidFile = join(process.cwd(), '.vizzly', 'server.pid'),
72
72
  readPid = readDaemonPidFile,
73
73
  findByPort = findDaemonPidByPort,
74
+ allowPortFallback = false,
74
75
  fileDeps = {}
75
76
  } = {}) {
76
77
  let pid = readPid(pidFile, fileDeps);
77
78
  if (pid) {
78
79
  return pid;
79
80
  }
81
+ if (!allowPortFallback) {
82
+ return null;
83
+ }
80
84
  return await findByPort(port);
81
85
  }
82
86
  export async function waitForProcessExit(pid, {