@vizzly-testing/cli 0.35.3-beta.0 → 0.35.3-beta.2

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.
@@ -1,10 +1,10 @@
1
1
  # Setup And CI
2
2
 
3
- Use this reference only when the user asks to initialize Vizzly, change CI, or
4
- troubleshoot configuration. These workflows mutate project files or external
5
- build state.
3
+ Use this reference only when the task includes initialization, CI changes, or
4
+ configuration troubleshooting. These workflows mutate files or external build
5
+ state.
6
6
 
7
- ## Local Setup
7
+ ## Initialize
8
8
 
9
9
  Initialize only with authorization:
10
10
 
@@ -12,57 +12,33 @@ Initialize only with authorization:
12
12
  vizzly init
13
13
  ```
14
14
 
15
- After setup, choose one local execution mode:
16
-
17
- ```bash
18
- vizzly tdd run "<existing visual test command>" --no-open
19
- ```
20
-
21
- or:
22
-
23
- ```bash
24
- vizzly tdd start --json
25
- <existing visual test command>
26
- vizzly tdd stop --json
27
- ```
28
-
29
- Do not run `tdd start` before `tdd run`; both own a local TDD server lifecycle.
15
+ Use [CLI context](cli-context.md) for local evidence and TDD lifecycle commands.
30
16
 
31
17
  ## Cloud CI
32
18
 
33
- Use a project-scoped token supplied by the user's existing secret-management
34
- system. Never print it, write it into the repository, or replace working
35
- credentials while debugging.
36
-
37
- Wrap the CI job's existing visual test command:
19
+ Use a project-scoped token from the repository's existing secret system. Never
20
+ print it or write it into the repository.
38
21
 
39
22
  ```bash
40
23
  vizzly run "<existing visual test command>" --wait --json
41
24
  ```
42
25
 
43
- ## Parallel CI
44
-
45
- Give every shard in one build the same parallel ID, unique to that workflow
46
- attempt. Run all shards without `--wait`, then finalize that shared ID once
47
- after every shard finishes:
26
+ For parallel CI, give every shard the same workflow-specific parallel ID. Run
27
+ shards without `--wait`, then finalize once after all shards finish:
48
28
 
49
29
  ```bash
50
30
  vizzly run "<shard test command>" --parallel-id "<shared-ci-run-id>" --json
51
31
  vizzly finalize "<shared-ci-run-id>" --json
52
32
  ```
53
33
 
54
- Do not create a different parallel ID per shard, and do not wait for a parallel
55
- build to complete before it has been finalized. Use one finalizer rather than
56
- having every shard race to finalize the build.
57
-
58
34
  ## Troubleshoot
59
35
 
60
- - Run `vizzly doctor` for local configuration checks.
61
- - Run `vizzly tdd status --json` for a local daemon started by the task.
62
- - Run `vizzly status <build-id> --json` for cloud lifecycle facts.
63
- - Run `vizzly context build <build-id> --source cloud --agent --json` for visual
64
- evidence.
65
- - If screenshots are absent, verify the existing SDK or integration, the test
66
- path that should capture them, and the active local or cloud session.
67
- - If authentication is absent, report it rather than initiating login unless
68
- setup is explicitly in scope.
36
+ - `vizzly doctor`: local configuration
37
+ - `vizzly tdd status --json`: a local daemon started by the task
38
+ - `vizzly status <build-id> --json`: cloud lifecycle
39
+ - `vizzly context build <build-id> --source cloud --agent --json`: visual
40
+ evidence
41
+
42
+ If screenshots are absent, verify the existing integration, the test path that
43
+ should capture them, and the active session. If authentication is absent,
44
+ report it rather than logging in unless setup is in scope.
@@ -1,13 +0,0 @@
1
- /**
2
- * Config Module - Public exports
3
- *
4
- * Provides functional configuration primitives:
5
- * - core.js: Pure functions for merging, serialization, validation results
6
- * - operations.js: Config operations with dependency injection
7
- */
8
-
9
- // Core pure functions
10
- export { buildGlobalConfigResult, buildMergedConfig, buildMergedConfigResult, buildProjectConfigResult, buildValidationFailure, buildValidationSuccess, CONFIG_DEFAULTS, deepMerge, extractCosmiconfigResult, extractEnvOverrides, getConfigFormat, READ_SCOPES, serializeConfig, serializeToJavaScript, serializeToJson, stringifyWithIndent, validateReadScope, validateWriteScope, WRITE_SCOPES } from './core.js';
11
-
12
- // Config operations (take dependencies as parameters)
13
- export { getConfig, getConfigSource, getGlobalConfig, getMergedConfig, getProjectConfig, updateConfig, updateGlobalConfig, updateProjectConfig, validateConfig, writeProjectConfigFile } from './operations.js';
@@ -1,13 +0,0 @@
1
- /**
2
- * Project Module - Public exports
3
- *
4
- * Provides functional project management primitives:
5
- * - core.js: Pure functions for validation, URL building, response extraction
6
- * - operations.js: Project operations with dependency injection
7
- */
8
-
9
- // Core pure functions
10
- export { buildBuildsQueryParams, buildBuildsUrl, buildMappingResult, buildNoApiServiceError, buildNoAuthError, buildOrgHeader, buildProjectFetchError, buildProjectUrl, buildTokenCreateError, buildTokenRevokeError, buildTokensFetchError, buildTokensUrl, enrichProjectsWithOrg, extractBuilds, extractOrganizations, extractProject, extractProjects, extractToken, extractTokens, mappingsToArray, validateDirectory, validateProjectData } from './core.js';
11
-
12
- // Project operations (take dependencies as parameters)
13
- export { createMapping, createProjectToken, getMapping, getProject, getProjectWithApiToken, getProjectWithOAuth, getRecentBuilds, getRecentBuildsWithApiToken, getRecentBuildsWithOAuth, listMappings, listProjects, listProjectsWithApiToken, listProjectsWithOAuth, listProjectTokens, removeMapping, revokeProjectToken, switchProject } from './operations.js';
package/dist/tdd/index.js DELETED
@@ -1,19 +0,0 @@
1
- /**
2
- * TDD Module Exports
3
- *
4
- * Re-exports all TDD functionality for clean imports.
5
- */
6
-
7
- export { calculateHotspotCoverage, shouldFilterAsHotspot } from './core/hotspot-coverage.js';
8
- // Core pure functions
9
- export { generateBaselineFilename, generateComparisonId, generateScreenshotSignature } from './core/signature.js';
10
-
11
- // Metadata I/O
12
- export { createEmptyBaselineMetadata, findScreenshotBySignature, loadBaselineMetadata, saveBaselineMetadata, upsertScreenshotInMetadata } from './metadata/baseline-metadata.js';
13
- export { createHotspotCache, getHotspotForScreenshot, loadHotspotMetadata, saveHotspotMetadata } from './metadata/hotspot-metadata.js';
14
- export { baselineMatchesSha, buildBaselineMetadataEntry, downloadBaselineImage, downloadBaselinesInBatches } from './services/baseline-downloader.js';
15
- // Services
16
- export { baselineExists, clearBaselineData, getBaselinePath, getCurrentPath, getDiffPath, initializeDirectories, promoteCurrentToBaseline, readBaseline, readCurrent, saveBaseline, saveCurrent } from './services/baseline-manager.js';
17
- export { buildErrorComparison, buildFailedComparison, buildNewComparison, buildPassedComparison, compareImages, isDimensionMismatchError } from './services/comparison-service.js';
18
- export { downloadHotspots } from './services/hotspot-service.js';
19
- export { buildResults, calculateSummary, findComparison, findComparisonById, getErrorComparisons, getFailedComparisons, getNewComparisons, isSuccessful } from './services/result-service.js';
@@ -1,151 +0,0 @@
1
- /**
2
- * Baseline Downloader
3
- *
4
- * Functions for downloading baseline images from the cloud API.
5
- * These are lower-level utilities - orchestration happens in TddService.
6
- */
7
-
8
- import { existsSync, writeFileSync } from 'node:fs';
9
- import { fetchWithTimeout } from '../../utils/fetch-utils.js';
10
-
11
- /**
12
- * Download a single baseline image
13
- *
14
- * @param {string} url - URL to download from
15
- * @param {string} destPath - Destination file path
16
- * @param {Object} options - Options
17
- * @param {number} options.timeout - Request timeout in ms (default: 30000)
18
- * @returns {Promise<{ success: boolean, error?: string }>}
19
- */
20
- export async function downloadBaselineImage(url, destPath, options = {}) {
21
- let {
22
- timeout = 30000
23
- } = options;
24
- try {
25
- let response = await fetchWithTimeout(url, {
26
- timeout
27
- });
28
- if (!response.ok) {
29
- return {
30
- success: false,
31
- error: `HTTP ${response.status}: ${response.statusText}`
32
- };
33
- }
34
- let buffer = Buffer.from(await response.arrayBuffer());
35
- writeFileSync(destPath, buffer);
36
- return {
37
- success: true
38
- };
39
- } catch (error) {
40
- return {
41
- success: false,
42
- error: error.message
43
- };
44
- }
45
- }
46
-
47
- /**
48
- * Check if a baseline already exists with matching SHA
49
- *
50
- * @param {string} filePath - Path to the baseline file
51
- * @param {string} expectedSha - Expected SHA256 hash
52
- * @param {Map<string, string>} shaMap - Map of filename -> sha256
53
- * @returns {boolean}
54
- */
55
- export function baselineMatchesSha(filePath, expectedSha, shaMap) {
56
- if (!existsSync(filePath) || !expectedSha) {
57
- return false;
58
- }
59
- let filename = filePath.split('/').pop();
60
- let storedSha = shaMap.get(filename);
61
- return storedSha === expectedSha;
62
- }
63
-
64
- /**
65
- * Download multiple baselines in batches
66
- *
67
- * @param {Array} screenshots - Screenshots to download
68
- * @param {Object} options - Options
69
- * @param {string} options.baselinePath - Path to baselines directory
70
- * @param {Map<string, string>} options.existingShaMap - Existing SHA map for skip logic
71
- * @param {number} options.batchSize - Concurrent downloads (default: 5)
72
- * @param {Function} options.onProgress - Progress callback (downloaded, skipped, errors, total)
73
- * @param {Function} options.getFilePath - Function to get file path for a screenshot
74
- * @returns {Promise<{ downloaded: number, skipped: number, errors: number }>}
75
- */
76
- export async function downloadBaselinesInBatches(screenshots, options = {}) {
77
- let {
78
- existingShaMap = new Map(),
79
- batchSize = 5,
80
- onProgress,
81
- getFilePath
82
- } = options;
83
- let downloaded = 0;
84
- let skipped = 0;
85
- let errors = 0;
86
- let total = screenshots.length;
87
-
88
- // Process in batches
89
- for (let i = 0; i < screenshots.length; i += batchSize) {
90
- let batch = screenshots.slice(i, i + batchSize);
91
- let batchPromises = batch.map(async screenshot => {
92
- let filePath = getFilePath(screenshot);
93
- let url = screenshot.original_url;
94
- if (!url) {
95
- errors++;
96
- return;
97
- }
98
-
99
- // Skip if SHA matches
100
- if (baselineMatchesSha(filePath, screenshot.sha256, existingShaMap)) {
101
- skipped++;
102
- downloaded++;
103
- return;
104
- }
105
- let result = await downloadBaselineImage(url, filePath);
106
- if (result.success) {
107
- downloaded++;
108
- } else {
109
- errors++;
110
- }
111
- });
112
- await Promise.all(batchPromises);
113
- if (onProgress) {
114
- onProgress(downloaded, skipped, errors, total);
115
- }
116
- }
117
- return {
118
- downloaded,
119
- skipped,
120
- errors
121
- };
122
- }
123
-
124
- /**
125
- * Build baseline metadata entry for a downloaded screenshot
126
- *
127
- * @param {Object} screenshot - Screenshot data from API
128
- * @param {string} filename - Local filename
129
- * @param {string} filePath - Full file path
130
- * @param {Object} buildInfo - Build information
131
- * @returns {Object} Metadata entry
132
- */
133
- export function buildBaselineMetadataEntry(screenshot, filename, filePath, buildInfo = {}) {
134
- return {
135
- name: screenshot.name,
136
- originalName: screenshot.name,
137
- sha256: screenshot.sha256,
138
- id: screenshot.id,
139
- filename,
140
- path: filePath,
141
- browser: screenshot.browser || screenshot.metadata?.browser,
142
- viewport_width: screenshot.viewport_width || screenshot.metadata?.viewport?.width || screenshot.properties?.viewport?.width,
143
- originalUrl: screenshot.original_url,
144
- fileSize: screenshot.file_size,
145
- dimensions: screenshot.dimensions,
146
- // Build info for tracking
147
- buildId: buildInfo.buildId,
148
- commitSha: buildInfo.commitSha,
149
- approvalStatus: screenshot.approval_status
150
- };
151
- }
@@ -1,58 +0,0 @@
1
- /**
2
- * Hotspot Service
3
- *
4
- * Functions for downloading and managing hotspot data from the cloud.
5
- * Hotspots identify regions that frequently change due to dynamic content.
6
- */
7
-
8
- import { saveHotspotMetadata } from '../metadata/hotspot-metadata.js';
9
-
10
- /**
11
- * Download hotspots for screenshots from cloud API
12
- *
13
- * @param {Object} options
14
- * @param {Object} options.api - ApiService instance
15
- * @param {string} options.workingDir - Working directory
16
- * @param {string[]} options.screenshotNames - Names of screenshots to get hotspots for
17
- * @returns {Promise<{ success: boolean, count: number, regionCount: number, error?: string }>}
18
- */
19
- export async function downloadHotspots(options) {
20
- let {
21
- api,
22
- workingDir,
23
- screenshotNames
24
- } = options;
25
- if (!screenshotNames || screenshotNames.length === 0) {
26
- return {
27
- success: true,
28
- count: 0,
29
- regionCount: 0
30
- };
31
- }
32
- try {
33
- let response = await api.getHotspots(screenshotNames);
34
- if (!response?.hotspots) {
35
- return {
36
- success: false,
37
- error: 'API returned no hotspot data'
38
- };
39
- }
40
-
41
- // Save hotspots to disk
42
- saveHotspotMetadata(workingDir, response.hotspots, response.summary);
43
-
44
- // Calculate stats
45
- let count = Object.keys(response.hotspots).length;
46
- let regionCount = Object.values(response.hotspots).reduce((sum, h) => sum + (h.regions?.length || 0), 0);
47
- return {
48
- success: true,
49
- count,
50
- regionCount
51
- };
52
- } catch (error) {
53
- return {
54
- success: false,
55
- error: error.message
56
- };
57
- }
58
- }
@@ -1,109 +0,0 @@
1
- /**
2
- * Environment detection utilities for handling CI/interactive environments
3
- */
4
-
5
- /**
6
- * Check if running in a CI environment
7
- * Based on common CI environment variables
8
- * @returns {boolean} True if running in CI
9
- */
10
- export function isCI() {
11
- return Boolean(process.env.CI ||
12
- // Generic CI flag
13
- process.env.CONTINUOUS_INTEGRATION ||
14
- // TravisCI, CircleCI
15
- process.env.BUILD_NUMBER ||
16
- // Jenkins
17
- process.env.GITHUB_ACTIONS ||
18
- // GitHub Actions
19
- process.env.GITLAB_CI ||
20
- // GitLab CI
21
- process.env.CIRCLECI ||
22
- // CircleCI
23
- process.env.TRAVIS ||
24
- // TravisCI
25
- process.env.APPVEYOR ||
26
- // AppVeyor
27
- process.env.CODEBUILD_BUILD_ID ||
28
- // AWS CodeBuild
29
- process.env.TEAMCITY_VERSION ||
30
- // TeamCity
31
- process.env.TF_BUILD ||
32
- // Azure DevOps
33
- process.env.DRONE ||
34
- // Drone CI
35
- process.env.BITBUCKET_BUILD_NUMBER // Bitbucket Pipelines
36
- );
37
- }
38
-
39
- /**
40
- * Check if stdout supports interactive features
41
- * @returns {boolean} True if interactive features are supported
42
- */
43
- export function isInteractiveTerminal() {
44
- return Boolean(process.stdout.isTTY && process.stdin.isTTY && (!process.env.TERM || process.env.TERM !== 'dumb'));
45
- }
46
-
47
- /**
48
- * Determine if interactive mode should be enabled
49
- * Takes into account CI detection, TTY support, and explicit flags
50
- * @param {Object} options
51
- * @param {boolean} [options.noInteractive] - Explicit flag to disable interactive mode
52
- * @param {boolean} [options.interactive] - Explicit flag to force interactive mode
53
- * @returns {boolean} True if interactive mode should be enabled
54
- */
55
- export function shouldUseInteractiveMode(options = {}) {
56
- // Explicit flags take priority
57
- // Commander.js sets interactive=false when --no-interactive is used
58
- if (options.interactive === false) {
59
- return false;
60
- }
61
- if (options.interactive === true) {
62
- return true;
63
- }
64
-
65
- // Legacy support for explicit noInteractive flag
66
- if (options.noInteractive === true) {
67
- return false;
68
- }
69
-
70
- // Auto-detect based on environment
71
- return !isCI() && isInteractiveTerminal();
72
- }
73
-
74
- /**
75
- * Get environment type for logging/debugging
76
- * @returns {string} Environment type description
77
- */
78
- export function getEnvironmentType() {
79
- if (isCI()) {
80
- return 'CI';
81
- }
82
- if (!isInteractiveTerminal()) {
83
- return 'non-interactive';
84
- }
85
- return 'interactive';
86
- }
87
-
88
- /**
89
- * Get detailed environment info for debugging
90
- * @returns {Object} Environment details
91
- */
92
- export function getEnvironmentDetails() {
93
- return {
94
- isCI: isCI(),
95
- isInteractiveTerminal: isInteractiveTerminal(),
96
- environmentType: getEnvironmentType(),
97
- stdoutIsTTY: Boolean(process.stdout.isTTY),
98
- stdinIsTTY: Boolean(process.stdin.isTTY),
99
- ciVars: {
100
- CI: process.env.CI,
101
- CONTINUOUS_INTEGRATION: process.env.CONTINUOUS_INTEGRATION,
102
- GITHUB_ACTIONS: process.env.GITHUB_ACTIONS,
103
- GITLAB_CI: process.env.GITLAB_CI,
104
- CIRCLECI: process.env.CIRCLECI,
105
- TRAVIS: process.env.TRAVIS
106
- },
107
- termType: process.env.TERM
108
- };
109
- }
@@ -1,4 +0,0 @@
1
- interface:
2
- display_name: "Vizzly"
3
- short_description: "Inspect and debug visual changes with Vizzly"
4
- default_prompt: "Use $vizzly to inspect the visual evidence for this UI change and explain what changed."