@vizzly-testing/cli 0.30.0 → 0.31.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 +9 -9
- package/dist/api/client.js +6 -3
- package/dist/cli.js +58 -29
- package/dist/client/index.js +21 -29
- package/dist/commands/api.js +145 -90
- package/dist/commands/baselines.js +78 -47
- package/dist/commands/builds.js +9 -8
- package/dist/commands/comparisons.js +7 -8
- package/dist/commands/config-cmd.js +15 -7
- package/dist/commands/context.js +329 -18
- package/dist/commands/doctor.js +116 -81
- package/dist/commands/init.js +202 -201
- package/dist/commands/login.js +134 -65
- package/dist/commands/logout.js +17 -6
- package/dist/commands/preview.js +29 -57
- package/dist/commands/project.js +120 -0
- package/dist/commands/projects.js +9 -2
- package/dist/commands/review.js +236 -202
- package/dist/commands/run.js +75 -72
- package/dist/commands/status.js +228 -150
- package/dist/commands/tdd-daemon-process.js +98 -0
- package/dist/commands/tdd-daemon.js +318 -212
- package/dist/commands/tdd.js +23 -8
- package/dist/commands/upload.js +10 -9
- package/dist/commands/whoami.js +63 -25
- package/dist/config/core.js +36 -35
- package/dist/context/local-workspace-provider.js +132 -12
- package/dist/context/provider-resolver.js +5 -1
- package/dist/index.js +5 -4
- package/dist/plugin-loader.js +55 -49
- package/dist/project/core.js +2 -2
- package/dist/project/operations.js +6 -3
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +7 -7
- package/dist/sdk/index.js +162 -107
- package/dist/server/handlers/tdd-handler.js +30 -54
- package/dist/server/routers/baseline.js +195 -178
- package/dist/server/routers/events.js +212 -174
- package/dist/server/routers/projects.js +14 -5
- package/dist/server/routers/screenshot.js +1 -31
- package/dist/services/build-manager.js +19 -97
- package/dist/services/config-service.js +26 -34
- package/dist/services/index.js +2 -4
- package/dist/services/static-report-generator.js +11 -12
- package/dist/services/test-runner.js +21 -27
- package/dist/tdd/index.js +1 -1
- package/dist/tdd/metadata/region-metadata.js +0 -37
- package/dist/tdd/runtime-context.js +78 -0
- package/dist/tdd/server-registry.js +38 -17
- package/dist/tdd/services/baseline-manager.js +23 -0
- package/dist/tdd/services/hotspot-service.js +0 -13
- package/dist/tdd/tdd-service.js +82 -113
- package/dist/types/client.d.ts +36 -1
- package/dist/types/index.d.ts +114 -22
- package/dist/types/sdk.d.ts +40 -0
- package/dist/uploader/core.js +5 -4
- package/dist/uploader/index.js +79 -2
- package/dist/uploader/operations.js +40 -5
- package/dist/utils/api-url.js +14 -0
- package/dist/utils/async-utils.js +26 -0
- package/dist/utils/ci-env.js +19 -12
- package/dist/utils/config-loader.js +39 -65
- package/dist/utils/config-schema.js +19 -55
- package/dist/utils/fetch-utils.js +12 -5
- package/dist/utils/git.js +35 -72
- package/dist/utils/global-config.js +20 -1
- package/dist/utils/output.js +19 -7
- package/dist/utils/patterns.js +14 -0
- package/dist/utils/project-link-store.js +105 -0
- package/dist/utils/screenshot-options.js +28 -0
- package/dist/utils/secret-store.js +65 -0
- package/package.json +11 -8
- package/dist/services/screenshot-server.js +0 -65
- package/dist/services/server-manager.js +0 -81
- package/dist/services/uploader.js +0 -100
- package/dist/tdd/services/region-service.js +0 -75
- package/dist/utils/build-history.js +0 -103
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server Manager Service
|
|
3
|
-
* Manages the HTTP server with functional handlers
|
|
4
|
-
*
|
|
5
|
-
* This class is a thin wrapper around the functional operations in
|
|
6
|
-
* src/server-manager/. It maintains backwards compatibility while
|
|
7
|
-
* delegating to pure functions for testability.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
11
|
-
import { createApiClient } from '../api/index.js';
|
|
12
|
-
import { createApiHandler } from '../server/handlers/api-handler.js';
|
|
13
|
-
import { createTddHandler } from '../server/handlers/tdd-handler.js';
|
|
14
|
-
import { createHttpServer } from '../server/http-server.js';
|
|
15
|
-
import { buildServerInterface, getTddResults, startServer, stopServer } from '../server-manager/index.js';
|
|
16
|
-
export class ServerManager {
|
|
17
|
-
constructor(config, options = {}) {
|
|
18
|
-
this.config = config;
|
|
19
|
-
this.httpServer = null;
|
|
20
|
-
this.handler = null;
|
|
21
|
-
this.services = options.services || {};
|
|
22
|
-
this.tddMode = false;
|
|
23
|
-
|
|
24
|
-
// Dependency injection for testing - defaults to real implementations
|
|
25
|
-
this.deps = options.deps || {
|
|
26
|
-
createHttpServer,
|
|
27
|
-
createTddHandler,
|
|
28
|
-
createApiHandler,
|
|
29
|
-
createApiClient,
|
|
30
|
-
fs: {
|
|
31
|
-
mkdirSync,
|
|
32
|
-
writeFileSync,
|
|
33
|
-
existsSync,
|
|
34
|
-
unlinkSync
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
async start(buildId = null, tddMode = false, setBaseline = false) {
|
|
39
|
-
this.buildId = buildId;
|
|
40
|
-
this.tddMode = tddMode;
|
|
41
|
-
this.setBaseline = setBaseline;
|
|
42
|
-
let result = await startServer({
|
|
43
|
-
config: this.config,
|
|
44
|
-
buildId,
|
|
45
|
-
tddMode,
|
|
46
|
-
setBaseline,
|
|
47
|
-
projectRoot: process.cwd(),
|
|
48
|
-
services: this.services,
|
|
49
|
-
deps: this.deps
|
|
50
|
-
});
|
|
51
|
-
this.httpServer = result.httpServer;
|
|
52
|
-
this.handler = result.handler;
|
|
53
|
-
}
|
|
54
|
-
async stop() {
|
|
55
|
-
await stopServer({
|
|
56
|
-
httpServer: this.httpServer,
|
|
57
|
-
handler: this.handler,
|
|
58
|
-
projectRoot: process.cwd(),
|
|
59
|
-
deps: this.deps
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Expose server interface for compatibility
|
|
64
|
-
get server() {
|
|
65
|
-
return buildServerInterface({
|
|
66
|
-
handler: this.handler,
|
|
67
|
-
httpServer: this.httpServer
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Get TDD results (comparisons, screenshot count, etc.)
|
|
73
|
-
* Only available in TDD mode after tests have run
|
|
74
|
-
*/
|
|
75
|
-
async getTddResults() {
|
|
76
|
-
return getTddResults({
|
|
77
|
-
tddMode: this.tddMode,
|
|
78
|
-
handler: this.handler
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vizzly Screenshot Uploader
|
|
3
|
-
* Handles screenshot uploads to the Vizzly platform
|
|
4
|
-
*
|
|
5
|
-
* This module is a thin wrapper around the functional operations in
|
|
6
|
-
* src/uploader/. It maintains backwards compatibility while
|
|
7
|
-
* delegating to pure functions for testability.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { readFile, stat } from 'node:fs/promises';
|
|
11
|
-
import { glob } from 'glob';
|
|
12
|
-
import { checkShas, createApiClient, createBuild } from '../api/index.js';
|
|
13
|
-
import { TimeoutError, UploadError, ValidationError } from '../errors/vizzly-error.js';
|
|
14
|
-
import { resolveBatchSize, resolveTimeout } from '../uploader/index.js';
|
|
15
|
-
import { upload as uploadOperation, waitForBuild as waitForBuildOperation } from '../uploader/operations.js';
|
|
16
|
-
import { getDefaultBranch } from '../utils/git.js';
|
|
17
|
-
import * as output from '../utils/output.js';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create a new uploader instance
|
|
21
|
-
*/
|
|
22
|
-
export function createUploader({
|
|
23
|
-
apiKey,
|
|
24
|
-
apiUrl,
|
|
25
|
-
userAgent,
|
|
26
|
-
command,
|
|
27
|
-
upload: uploadConfig = {}
|
|
28
|
-
} = {}, options = {}) {
|
|
29
|
-
let signal = options.signal || new AbortController().signal;
|
|
30
|
-
let client = createApiClient({
|
|
31
|
-
baseUrl: apiUrl,
|
|
32
|
-
token: apiKey,
|
|
33
|
-
command: command || 'upload',
|
|
34
|
-
sdkUserAgent: userAgent,
|
|
35
|
-
allowNoToken: true
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Resolve tunable parameters
|
|
39
|
-
let batchSize = resolveBatchSize(options, uploadConfig);
|
|
40
|
-
let timeout = resolveTimeout(options, uploadConfig);
|
|
41
|
-
|
|
42
|
-
// Dependency injection for testing
|
|
43
|
-
let deps = options.deps || {
|
|
44
|
-
client,
|
|
45
|
-
createBuild,
|
|
46
|
-
getDefaultBranch,
|
|
47
|
-
glob,
|
|
48
|
-
readFile,
|
|
49
|
-
stat,
|
|
50
|
-
checkShas,
|
|
51
|
-
createError: (message, code, context) => {
|
|
52
|
-
let error = new UploadError(message, context);
|
|
53
|
-
error.code = code;
|
|
54
|
-
return error;
|
|
55
|
-
},
|
|
56
|
-
createValidationError: (message, context) => new ValidationError(message, context),
|
|
57
|
-
createUploadError: (message, context) => new UploadError(message, context),
|
|
58
|
-
createTimeoutError: (message, context) => new TimeoutError(message, context),
|
|
59
|
-
output
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Upload screenshots to Vizzly
|
|
64
|
-
*/
|
|
65
|
-
async function upload(uploadOptions) {
|
|
66
|
-
return uploadOperation({
|
|
67
|
-
uploadOptions,
|
|
68
|
-
config: {
|
|
69
|
-
apiKey,
|
|
70
|
-
apiUrl
|
|
71
|
-
},
|
|
72
|
-
signal,
|
|
73
|
-
batchSize,
|
|
74
|
-
deps: {
|
|
75
|
-
...deps,
|
|
76
|
-
client: deps.client || client
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Wait for a build to complete
|
|
83
|
-
*/
|
|
84
|
-
async function waitForBuild(buildId, waitTimeout = timeout) {
|
|
85
|
-
return waitForBuildOperation({
|
|
86
|
-
buildId,
|
|
87
|
-
timeout: waitTimeout,
|
|
88
|
-
signal,
|
|
89
|
-
client: deps.client || client,
|
|
90
|
-
deps: {
|
|
91
|
-
createError: deps.createError,
|
|
92
|
-
createTimeoutError: deps.createTimeoutError
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
upload,
|
|
98
|
-
waitForBuild
|
|
99
|
-
};
|
|
100
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Region Service
|
|
3
|
-
*
|
|
4
|
-
* Functions for downloading and managing user-defined hotspot regions from the cloud.
|
|
5
|
-
* Regions are 2D bounding boxes that users have confirmed as dynamic content areas.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { saveRegionMetadata } from '../metadata/region-metadata.js';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Download user-defined regions 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 regions for
|
|
17
|
-
* @param {boolean} options.includeCandidates - Include candidate regions (default: false)
|
|
18
|
-
* @returns {Promise<{ success: boolean, count: number, regionCount: number, error?: string }>}
|
|
19
|
-
*/
|
|
20
|
-
export async function downloadRegions(options) {
|
|
21
|
-
let {
|
|
22
|
-
api,
|
|
23
|
-
workingDir,
|
|
24
|
-
screenshotNames,
|
|
25
|
-
includeCandidates = false
|
|
26
|
-
} = options;
|
|
27
|
-
if (!screenshotNames || screenshotNames.length === 0) {
|
|
28
|
-
return {
|
|
29
|
-
success: true,
|
|
30
|
-
count: 0,
|
|
31
|
-
regionCount: 0
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
let response = await api.getRegions(screenshotNames, {
|
|
36
|
-
includeCandidates
|
|
37
|
-
});
|
|
38
|
-
if (!response?.regions) {
|
|
39
|
-
return {
|
|
40
|
-
success: false,
|
|
41
|
-
error: 'API returned no region data'
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Save regions to disk
|
|
46
|
-
saveRegionMetadata(workingDir, response.regions, response.summary);
|
|
47
|
-
|
|
48
|
-
// Calculate stats
|
|
49
|
-
let count = Object.keys(response.regions).length;
|
|
50
|
-
let regionCount = response.summary?.total_regions || 0;
|
|
51
|
-
return {
|
|
52
|
-
success: true,
|
|
53
|
-
count,
|
|
54
|
-
regionCount
|
|
55
|
-
};
|
|
56
|
-
} catch (error) {
|
|
57
|
-
return {
|
|
58
|
-
success: false,
|
|
59
|
-
error: error.message
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Extract screenshot names from a list of screenshots
|
|
66
|
-
*
|
|
67
|
-
* @param {Array} screenshots - Screenshots with name property
|
|
68
|
-
* @returns {string[]}
|
|
69
|
-
*/
|
|
70
|
-
export function extractScreenshotNames(screenshots) {
|
|
71
|
-
if (!screenshots || !Array.isArray(screenshots)) {
|
|
72
|
-
return [];
|
|
73
|
-
}
|
|
74
|
-
return screenshots.map(s => s.name).filter(Boolean);
|
|
75
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Archive a build to history directory
|
|
6
|
-
* @param {string} workingDir - Working directory
|
|
7
|
-
* @param {string} buildId - Build ID to archive
|
|
8
|
-
* @param {Array} builds - Build data
|
|
9
|
-
* @param {Array} comparisons - Comparison data
|
|
10
|
-
* @param {Object} summary - Summary stats
|
|
11
|
-
* @param {number} maxHistory - Maximum number of builds to keep (default: 3)
|
|
12
|
-
*/
|
|
13
|
-
export function archiveBuild(workingDir, buildId, builds, comparisons, summary, maxHistory = 3) {
|
|
14
|
-
const historyDir = join(workingDir, '.vizzly', 'history');
|
|
15
|
-
|
|
16
|
-
// Create history directory if it doesn't exist
|
|
17
|
-
if (!existsSync(historyDir)) {
|
|
18
|
-
mkdirSync(historyDir, {
|
|
19
|
-
recursive: true
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Save current build to history
|
|
24
|
-
const buildDir = join(historyDir, buildId);
|
|
25
|
-
if (!existsSync(buildDir)) {
|
|
26
|
-
mkdirSync(buildDir, {
|
|
27
|
-
recursive: true
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
const buildData = {
|
|
31
|
-
buildId,
|
|
32
|
-
timestamp: Date.now(),
|
|
33
|
-
builds,
|
|
34
|
-
comparisons,
|
|
35
|
-
summary
|
|
36
|
-
};
|
|
37
|
-
writeFileSync(join(buildDir, 'report.json'), JSON.stringify(buildData, null, 2));
|
|
38
|
-
|
|
39
|
-
// Clean up old builds (keep last N)
|
|
40
|
-
cleanupOldBuilds(historyDir, maxHistory);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Get list of archived builds
|
|
45
|
-
* @param {string} workingDir - Working directory
|
|
46
|
-
* @returns {Array} Array of build metadata
|
|
47
|
-
*/
|
|
48
|
-
export function getArchivedBuilds(workingDir) {
|
|
49
|
-
const historyDir = join(workingDir, '.vizzly', 'history');
|
|
50
|
-
if (!existsSync(historyDir)) {
|
|
51
|
-
return [];
|
|
52
|
-
}
|
|
53
|
-
try {
|
|
54
|
-
const buildDirs = readdirSync(historyDir, {
|
|
55
|
-
withFileTypes: true
|
|
56
|
-
}).filter(dirent => dirent.isDirectory()).map(dirent => dirent.name).sort().reverse(); // Newest first
|
|
57
|
-
|
|
58
|
-
return buildDirs.map(buildId => {
|
|
59
|
-
const reportPath = join(historyDir, buildId, 'report.json');
|
|
60
|
-
if (existsSync(reportPath)) {
|
|
61
|
-
try {
|
|
62
|
-
const data = JSON.parse(require('node:fs').readFileSync(reportPath, 'utf8'));
|
|
63
|
-
return {
|
|
64
|
-
buildId: data.buildId,
|
|
65
|
-
timestamp: data.timestamp,
|
|
66
|
-
summary: data.summary
|
|
67
|
-
};
|
|
68
|
-
} catch {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
73
|
-
}).filter(Boolean);
|
|
74
|
-
} catch {
|
|
75
|
-
return [];
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Remove old builds, keeping only the last N
|
|
81
|
-
* @private
|
|
82
|
-
*/
|
|
83
|
-
function cleanupOldBuilds(historyDir, maxHistory) {
|
|
84
|
-
try {
|
|
85
|
-
const buildDirs = readdirSync(historyDir, {
|
|
86
|
-
withFileTypes: true
|
|
87
|
-
}).filter(dirent => dirent.isDirectory()).map(dirent => dirent.name).sort().reverse(); // Newest first
|
|
88
|
-
|
|
89
|
-
// Remove builds beyond maxHistory
|
|
90
|
-
if (buildDirs.length > maxHistory) {
|
|
91
|
-
const toRemove = buildDirs.slice(maxHistory);
|
|
92
|
-
toRemove.forEach(buildId => {
|
|
93
|
-
const buildDir = join(historyDir, buildId);
|
|
94
|
-
rmSync(buildDir, {
|
|
95
|
-
recursive: true,
|
|
96
|
-
force: true
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
} catch {
|
|
101
|
-
// Ignore cleanup errors
|
|
102
|
-
}
|
|
103
|
-
}
|