@vizzly-testing/cli 0.38.0 → 0.39.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.
- package/README.md +222 -183
- package/dist/auth/client.js +4 -1
- package/dist/auth/core.js +64 -3
- package/dist/auth/operations.js +21 -11
- package/dist/cli.js +45 -53
- package/dist/commands/init.js +156 -483
- package/dist/commands/login.js +71 -137
- package/dist/commands/project.js +128 -44
- package/dist/commands/run.js +2 -2
- package/dist/commands/tdd.js +89 -38
- package/dist/commands/upload.js +2 -2
- package/dist/init/agents.js +99 -0
- package/dist/init/core.js +31 -0
- package/dist/init/prompts.js +44 -0
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +31 -127
- package/dist/server/handlers/tdd-handler.js +33 -2
- package/dist/server/routers/auth.js +32 -42
- package/dist/services/index.js +8 -2
- package/dist/tdd/services/comparison-service.js +4 -1
- package/dist/tdd/tdd-service.js +14 -2
- package/dist/test-runner/core.js +28 -3
- package/dist/test-runner/operations.js +41 -20
- package/dist/types/index.d.ts +8 -0
- package/dist/uploader/core.js +1 -0
- package/dist/utils/browser.js +7 -1
- package/dist/utils/config-discovery.js +73 -0
- package/dist/utils/config-loader.js +29 -8
- package/dist/utils/config-schema.js +5 -0
- package/dist/utils/file-changes.js +105 -0
- package/dist/utils/login-poll-wait.js +50 -0
- package/dist/utils/project-binding.js +96 -0
- package/dist/utils/project-link-store.js +13 -7
- package/package.json +27 -27
- package/skills/vizzly/SKILL.md +4 -3
- package/skills/vizzly/references/ci.md +33 -0
- package/skills/vizzly/references/cli-context.md +81 -71
- package/skills/vizzly/references/dynamic-content.md +16 -17
- package/skills/vizzly/references/sdks.md +22 -22
- package/skills/vizzly/references/setup-ci.md +0 -44
package/dist/cli.js
CHANGED
|
@@ -28,9 +28,7 @@ import { whoamiCommand } from './commands/whoami.js';
|
|
|
28
28
|
import { createPluginServices } from './plugin-api.js';
|
|
29
29
|
import { loadPlugins } from './plugin-loader.js';
|
|
30
30
|
import { createServices } from './services/index.js';
|
|
31
|
-
import { generateStaticReport, getReportFileUrl } from './services/static-report-generator.js';
|
|
32
31
|
import { withTimeout } from './utils/async-utils.js';
|
|
33
|
-
import { openBrowser } from './utils/browser.js';
|
|
34
32
|
import { colors } from './utils/colors.js';
|
|
35
33
|
import { loadConfig } from './utils/config-loader.js';
|
|
36
34
|
import { getContext } from './utils/context.js';
|
|
@@ -430,38 +428,49 @@ output.configure({
|
|
|
430
428
|
color: colorOverride,
|
|
431
429
|
json: jsonArg
|
|
432
430
|
});
|
|
433
|
-
let
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
let
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
431
|
+
let setupCommand = findRequestedCommand(process.argv, new Set(['init', 'project']))?.name;
|
|
432
|
+
let needsRuntimeBootstrap = !['init', 'project'].includes(setupCommand);
|
|
433
|
+
if (needsRuntimeBootstrap) {
|
|
434
|
+
let config = await loadConfig(configPath, {
|
|
435
|
+
token: tokenArg
|
|
436
|
+
});
|
|
437
|
+
let services = createServices(config);
|
|
438
|
+
let pluginServices = createPluginServices(services);
|
|
439
|
+
try {
|
|
440
|
+
let plugins = await loadPlugins(configPath, config);
|
|
441
|
+
for (let plugin of plugins) {
|
|
442
|
+
try {
|
|
443
|
+
// Add timeout protection for plugin registration (5 seconds)
|
|
444
|
+
let registerPromise = plugin.register(program, {
|
|
445
|
+
config,
|
|
446
|
+
services: pluginServices,
|
|
447
|
+
output
|
|
448
|
+
});
|
|
449
|
+
await withTimeout(registerPromise, 5000, 'Plugin registration timeout (5s)');
|
|
450
|
+
output.debug(`Registered plugin: ${plugin.name}`);
|
|
451
|
+
} catch (error) {
|
|
452
|
+
output.warn(`Failed to register plugin ${plugin.name}: ${error.message}`);
|
|
453
|
+
}
|
|
453
454
|
}
|
|
455
|
+
} catch (error) {
|
|
456
|
+
output.debug(`Plugin loading failed: ${error.message}`);
|
|
454
457
|
}
|
|
455
|
-
} catch (error) {
|
|
456
|
-
output.debug(`Plugin loading failed: ${error.message}`);
|
|
457
458
|
}
|
|
458
|
-
program.command('init').description('Initialize Vizzly in your project').option('--force', 'Overwrite existing configuration').option('--agent-skill', 'Install
|
|
459
|
+
program.command('init').description('Initialize Vizzly in your project').option('--quick', 'Create an editable config without the setup wizard').option('--force', 'Overwrite existing configuration').option('--agent-skill', 'Install the Vizzly agent skill during setup').option('--agent-guidance', 'Install the Vizzly skill and AGENTS.md guidance during setup').option('--skip-agent-skill', 'Skip the Vizzly agent skill prompt').action(async options => {
|
|
459
460
|
let globalOptions = getGlobalOptions();
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
461
|
+
try {
|
|
462
|
+
let result = await init({
|
|
463
|
+
...globalOptions,
|
|
464
|
+
...options
|
|
465
|
+
});
|
|
466
|
+
process.exitCode = result.exitCode || 0;
|
|
467
|
+
} catch (error) {
|
|
468
|
+
output.error(error.message, error, {
|
|
469
|
+
changes: error.completed || []
|
|
470
|
+
});
|
|
471
|
+
if (error.completed?.length) output.list(error.completed.map(change => change.path));
|
|
472
|
+
process.exitCode = error.code === 'INIT_INPUT_REQUIRED' ? 2 : 1;
|
|
473
|
+
}
|
|
465
474
|
});
|
|
466
475
|
program.command('upload').description('Upload screenshots to Vizzly').argument('<path>', 'Path to screenshots directory').option('-b, --build-name <name>', 'Build name for grouping').option('-m, --metadata <json>', 'Additional metadata as JSON').option('--batch-size <n>', 'Upload batch size', Number).option('--upload-timeout <ms>', 'Upload timeout in milliseconds', Number).option('--branch <branch>', 'Git branch').option('--commit <sha>', 'Git commit SHA').option('--message <msg>', 'Commit message').option('--environment <env>', 'Environment name', 'test').option('--threshold <number>', 'Comparison threshold', Number).option('--min-cluster-size <pixels>', 'Minimum changed-pixel cluster size', Number).option('--token <token>', 'API token override').option('--wait', 'Wait for build completion').option('--upload-all', 'Upload all screenshots without SHA deduplication').option('--parallel-id <id>', 'Unique identifier for parallel test execution').action(async (path, options) => {
|
|
467
476
|
let globalOptions = getGlobalOptions();
|
|
@@ -555,7 +564,10 @@ tddCmd.command('run <command>').description('Run tests once in TDD mode with loc
|
|
|
555
564
|
const {
|
|
556
565
|
result,
|
|
557
566
|
cleanup
|
|
558
|
-
} = await tddCommand(command,
|
|
567
|
+
} = await tddCommand(command, {
|
|
568
|
+
...options,
|
|
569
|
+
autoPort: options.port === undefined
|
|
570
|
+
}, globalOptions);
|
|
559
571
|
|
|
560
572
|
// Track cleanup state to prevent double cleanup
|
|
561
573
|
let isCleaningUp = false;
|
|
@@ -575,26 +587,6 @@ tddCmd.command('run <command>').description('Run tests once in TDD mode with loc
|
|
|
575
587
|
process.once('SIGINT', sigintHandler);
|
|
576
588
|
process.once('SIGTERM', sigtermHandler);
|
|
577
589
|
|
|
578
|
-
// If the run captured screenshots, generate a static report for review.
|
|
579
|
-
let hasComparisons = result?.screenshotsCaptured > 0 || result?.comparisons?.length > 0 || result?.summary?.total > 0;
|
|
580
|
-
if (hasComparisons) {
|
|
581
|
-
// Note: Tests have completed at this point, so report-data.json is stable.
|
|
582
|
-
// The report reflects the final state of all comparisons.
|
|
583
|
-
const reportResult = await generateStaticReport(process.cwd());
|
|
584
|
-
if (reportResult.success) {
|
|
585
|
-
const reportUrl = getReportFileUrl(reportResult.reportPath);
|
|
586
|
-
output.print(` ${colors.brand.textTertiary('→')} Report: ${colors.info(reportUrl)}`);
|
|
587
|
-
output.blank();
|
|
588
|
-
|
|
589
|
-
// Open report in browser unless --no-open
|
|
590
|
-
if (options.open !== false) {
|
|
591
|
-
await openBrowser(reportUrl);
|
|
592
|
-
}
|
|
593
|
-
} else {
|
|
594
|
-
output.warn(`Failed to generate static report: ${reportResult.error}`);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
590
|
// Remove signal handlers before normal cleanup to prevent double cleanup
|
|
599
591
|
process.off('SIGINT', sigintHandler);
|
|
600
592
|
process.off('SIGTERM', sigtermHandler);
|
|
@@ -950,7 +942,7 @@ program.command('whoami').description('Show current authentication status and us
|
|
|
950
942
|
|
|
951
943
|
// Save PATH for the menubar app before commands read or update credentials
|
|
952
944
|
// in the same config file.
|
|
953
|
-
await saveUserPath().catch(() => {});
|
|
945
|
+
if (needsRuntimeBootstrap) await saveUserPath().catch(() => {});
|
|
954
946
|
let commandNames = new Set(program.commands.map(command => command.name()));
|
|
955
947
|
let nestedCommandNames = new Map(program.commands.map(command => [command.name(), new Set(command.registeredArguments.length ? [] : command.commands.map(subcommand => subcommand.name()))]));
|
|
956
948
|
let normalizedArgv = normalizeJsonArgv(process.argv, commandNames);
|
|
@@ -971,7 +963,7 @@ if (requestedCommand && !requestedCommand.known) {
|
|
|
971
963
|
process.exit(1);
|
|
972
964
|
}
|
|
973
965
|
try {
|
|
974
|
-
program.
|
|
966
|
+
await program.parseAsync(normalizedArgv);
|
|
975
967
|
} catch (error) {
|
|
976
968
|
if (error.exitCode == null) {
|
|
977
969
|
output.error('Unexpected CLI error', error);
|