@vizzly-testing/cli 0.39.0 → 0.39.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.
- package/README.md +12 -3
- package/bin/vizzly.js +1 -1
- package/dist/{cli.js → adapters/cli/cli.js} +50 -35
- package/dist/adapters/cli/setup.js +34 -0
- package/dist/{client → adapters/client}/index.js +6 -7
- package/dist/adapters/http/cloud-proxy.js +29 -0
- package/dist/{server → adapters/http}/http-server.js +12 -13
- package/dist/{server-manager/operations.js → adapters/http/server-lifecycle.js} +10 -13
- package/dist/{server-manager/index.js → adapters/http/server-manager.js} +23 -27
- package/dist/{plugin-api.js → adapters/plugins/plugin-api.js} +1 -1
- package/dist/{plugin-loader.js → adapters/plugins/plugin-loader.js} +1 -1
- package/dist/{services/index.js → adapters/plugins/services.js} +4 -9
- package/dist/adapters/sdk/index.d.ts +89 -0
- package/dist/{sdk → adapters/sdk}/index.js +22 -19
- package/dist/{commands/api.js → domains/api/api.commands.js} +3 -3
- package/dist/{auth/client.js → domains/auth/auth-client.js} +4 -3
- package/dist/domains/auth/auth-errors.js +50 -0
- package/dist/domains/auth/auth-headers.js +41 -0
- package/dist/domains/auth/auth.normalizers.js +50 -0
- package/dist/{server/routers/auth.js → domains/auth/auth.routes.js} +9 -9
- package/dist/domains/auth/auth.rules.js +19 -0
- package/dist/domains/auth/auth.serializers.js +32 -0
- package/dist/{auth/operations.js → domains/auth/auth.service.js} +115 -17
- package/dist/domains/auth/auth.validations.js +21 -0
- package/dist/{api/client.js → domains/auth/cloud-client.js} +11 -18
- package/dist/{commands/login.js → domains/auth/login.commands.js} +11 -8
- package/dist/{commands/logout.js → domains/auth/logout.commands.js} +6 -4
- package/dist/domains/auth/token-store.js +81 -0
- package/dist/{commands/whoami.js → domains/auth/whoami.commands.js} +6 -3
- package/dist/domains/baselines/baseline-download.js +192 -0
- package/dist/domains/baselines/baseline-write.js +22 -0
- package/dist/{server/routers/baseline.js → domains/baselines/baseline.routes.js} +3 -3
- package/dist/{tdd/services/baseline-manager.js → domains/baselines/baseline.service.js} +1 -39
- package/dist/{commands/baselines.js → domains/baselines/baselines.commands.js} +3 -3
- package/dist/domains/baselines/cloud-baselines.js +9 -0
- package/dist/domains/baselines/cloud-baselines.routes.js +42 -0
- package/dist/domains/builds/build.normalizers.js +35 -0
- package/dist/domains/builds/build.serializers.js +164 -0
- package/dist/domains/builds/build.service.js +119 -0
- package/dist/domains/builds/build.validations.js +39 -0
- package/dist/{commands/builds.js → domains/builds/builds.commands.js} +6 -74
- package/dist/domains/builds/cloud-builds.routes.js +31 -0
- package/dist/{commands/finalize.js → domains/builds/finalize.commands.js} +5 -17
- package/dist/{commands/status.js → domains/builds/status.commands.js} +9 -120
- package/dist/domains/comparisons/cloud-comparisons.js +43 -0
- package/dist/{tdd/services/comparison-service.js → domains/comparisons/comparison.service.js} +1 -1
- package/dist/{commands/comparisons.js → domains/comparisons/comparisons.commands.js} +7 -5
- package/dist/domains/config/config-defaults.js +27 -0
- package/dist/{utils → domains/config}/config-discovery.js +1 -9
- package/dist/{utils → domains/config}/config-loader.js +10 -9
- package/dist/{commands/config-cmd.js → domains/config/config.commands.js} +4 -4
- package/dist/{types → domains/config}/config.d.ts +3 -3
- package/dist/domains/config/config.normalizers.js +126 -0
- package/dist/{server/routers/config.js → domains/config/config.routes.js} +13 -13
- package/dist/domains/config/config.rules.js +47 -0
- package/dist/domains/config/config.serializers.js +202 -0
- package/dist/domains/config/config.service.js +625 -0
- package/dist/{utils/config-schema.js → domains/config/config.validations.js} +9 -9
- package/dist/{utils/environment-config.js → domains/config/environment.js} +1 -8
- package/dist/domains/context/compact-context.validations.js +60 -0
- package/dist/domains/context/context-presentation.js +368 -0
- package/dist/domains/context/context.commands.js +385 -0
- package/dist/domains/context/context.serializers.js +157 -0
- package/dist/domains/context/context.service.js +76 -0
- package/dist/domains/context/context.validations.js +100 -0
- package/dist/{context → domains/context}/local-workspace-provider.js +1 -1
- package/dist/{commands/doctor.js → domains/diagnostics/doctor.commands.js} +6 -5
- package/dist/{server/routers/health.js → domains/diagnostics/health.routes.js} +11 -20
- package/dist/{commands/preview.js → domains/previews/preview.commands.js} +9 -7
- package/dist/domains/previews/preview.service.js +37 -0
- package/dist/domains/projects/cloud-projects.routes.js +28 -0
- package/dist/{commands/orgs.js → domains/projects/orgs.commands.js} +6 -8
- package/dist/{utils → domains/projects}/project-binding.js +3 -3
- package/dist/domains/projects/project-errors.js +61 -0
- package/dist/{utils → domains/projects}/project-link-store.js +2 -2
- package/dist/domains/projects/project-requests.js +61 -0
- package/dist/{commands/project.js → domains/projects/project.commands.js} +9 -41
- package/dist/domains/projects/project.normalizers.js +69 -0
- package/dist/{project/operations.js → domains/projects/project.service.js} +89 -16
- package/dist/domains/projects/project.validations.js +31 -0
- package/dist/{commands/projects.js → domains/projects/projects.commands.js} +6 -8
- package/dist/{server/routers/projects.js → domains/projects/projects.routes.js} +5 -5
- package/dist/domains/reports/dashboard.routes.js +96 -0
- package/dist/{server/routers/events.js → domains/reports/events.routes.js} +14 -36
- package/dist/domains/reports/report-assets.routes.js +81 -0
- package/dist/domains/reports/report-state.js +51 -0
- package/dist/domains/reports/report-store.js +85 -0
- package/dist/domains/reports/report.serializers.js +99 -0
- package/dist/{services/static-report-generator.js → domains/reports/report.service.js} +11 -33
- package/dist/{commands/review.js → domains/review/review.commands.js} +4 -4
- package/dist/domains/runs/run-environment.js +69 -0
- package/dist/{services/test-runner.js → domains/runs/run-events.js} +11 -9
- package/dist/{commands/run.js → domains/runs/run.commands.js} +16 -13
- package/dist/domains/runs/run.normalizers.js +8 -0
- package/dist/domains/runs/run.rules.js +56 -0
- package/dist/domains/runs/run.serializers.js +101 -0
- package/dist/{test-runner/operations.js → domains/runs/run.service.js} +35 -7
- package/dist/domains/runs/run.validations.js +17 -0
- package/dist/{utils/session.js → domains/runs/session-store.js} +3 -2
- package/dist/{server/handlers/api-handler.js → domains/screenshots/cloud-handler.js} +4 -4
- package/dist/domains/screenshots/cloud-screenshots.js +109 -0
- package/dist/{utils/file-helpers.js → domains/screenshots/image-files.js} +1 -1
- package/dist/domains/screenshots/screenshot-hash.js +53 -0
- package/dist/domains/screenshots/screenshot-server-options.js +28 -0
- package/dist/{screenshot-server/operations.js → domains/screenshots/screenshot-server.js} +14 -1
- package/dist/domains/screenshots/screenshot.normalizers.js +40 -0
- package/dist/{server/routers/screenshot.js → domains/screenshots/screenshot.routes.js} +4 -4
- package/dist/domains/screenshots/screenshot.serializers.js +146 -0
- package/dist/{utils/security.js → domains/screenshots/screenshot.validations.js} +20 -70
- package/dist/{init/agents.js → domains/setup/agent-setup.js} +54 -14
- package/dist/{commands/init.js → domains/setup/init.commands.js} +11 -44
- package/dist/{server/handlers/tdd-handler.js → domains/tdd/screenshot-handler.js} +46 -281
- package/dist/{commands/tdd-daemon.js → domains/tdd/tdd-daemon.commands.js} +6 -6
- package/dist/{commands/tdd-screenshots.js → domains/tdd/tdd-screenshots.commands.js} +5 -5
- package/dist/{commands/tdd.js → domains/tdd/tdd.commands.js} +16 -15
- package/dist/{tdd/tdd-service.js → domains/tdd/tdd.service.js} +81 -405
- package/dist/domains/updates/cli-update.js +103 -0
- package/dist/domains/updates/update-check.js +2 -0
- package/dist/domains/updates/update-notifications.js +175 -0
- package/dist/domains/uploads/upload-defaults.js +4 -0
- package/dist/domains/uploads/upload-files.js +32 -0
- package/dist/domains/uploads/upload-settings.js +21 -0
- package/dist/domains/uploads/upload-timing.js +18 -0
- package/dist/{commands/upload.js → domains/uploads/upload.commands.js} +11 -9
- package/dist/domains/uploads/upload.normalizers.js +60 -0
- package/dist/domains/uploads/upload.serializers.js +179 -0
- package/dist/{uploader/operations.js → domains/uploads/upload.service.js} +85 -28
- package/dist/domains/uploads/upload.validations.js +77 -0
- package/dist/index.js +12 -12
- package/dist/reporter/reporter-bundle.iife.js +86 -86
- package/dist/reporter-ssr/ssr-entry.js +3 -6
- package/dist/shared/cloud-protocol.js +171 -0
- package/dist/{utils → shared}/file-changes.js +1 -1
- package/dist/shared/files.js +9 -0
- package/dist/{utils → shared}/global-config.js +7 -78
- package/dist/shared/log-level.js +7 -0
- package/dist/{utils → shared}/output.js +1 -1
- package/dist/{utils → shared}/package-info.js +5 -4
- package/dist/shared/paths.js +57 -0
- package/package.json +37 -36
- package/dist/api/core.js +0 -382
- package/dist/api/endpoints.js +0 -421
- package/dist/api/index.js +0 -19
- package/dist/auth/core.js +0 -237
- package/dist/auth/index.js +0 -31
- package/dist/commands/context.js +0 -1066
- package/dist/config/core.js +0 -439
- package/dist/config/operations.js +0 -327
- package/dist/project/core.js +0 -211
- package/dist/screenshot-server/core.js +0 -161
- package/dist/screenshot-server/index.js +0 -11
- package/dist/server/routers/assets.js +0 -95
- package/dist/server/routers/cloud-proxy.js +0 -146
- package/dist/server/routers/dashboard.js +0 -172
- package/dist/services/auth-service.js +0 -117
- package/dist/services/config-service.js +0 -313
- package/dist/services/project-service.js +0 -88
- package/dist/test-runner/core.js +0 -293
- package/dist/test-runner/index.js +0 -13
- package/dist/types/sdk.d.ts +0 -186
- package/dist/uploader/core.js +0 -432
- package/dist/uploader/index.js +0 -88
- /package/dist/{types/client.d.ts → adapters/client/index.d.ts} +0 -0
- /package/dist/{server → adapters/http}/middleware/cors.js +0 -0
- /package/dist/{server → adapters/http}/middleware/json-parser.js +0 -0
- /package/dist/{server → adapters/http}/middleware/response.js +0 -0
- /package/dist/{server-manager/core.js → adapters/http/server-options.js} +0 -0
- /package/dist/{utils/login-poll-wait.js → domains/auth/login-wait.js} +0 -0
- /package/dist/{tdd/metadata → domains/baselines}/baseline-metadata.js +0 -0
- /package/dist/{services/build-manager.js → domains/builds/build-info.js} +0 -0
- /package/dist/{tdd/services/result-service.js → domains/comparisons/comparison-results.js} +0 -0
- /package/dist/{init/core.js → domains/config/config-plan.js} +0 -0
- /package/dist/{utils/config-helpers.js → domains/config/config.js} +0 -0
- /package/dist/{utils/visual-context-normalizers.js → domains/context/context.normalizers.js} +0 -0
- /package/dist/{context → domains/context}/provider-resolver.js +0 -0
- /package/dist/{utils/context.js → domains/diagnostics/environment-context.js} +0 -0
- /package/dist/{utils/report-data.js → domains/reports/report.normalizers.js} +0 -0
- /package/dist/{utils/image-input-detector.js → domains/screenshots/image-input.js} +0 -0
- /package/dist/{tdd/core/signature.js → domains/screenshots/screenshot-identity.js} +0 -0
- /package/dist/{utils → domains/screenshots}/screenshot-options.js +0 -0
- /package/dist/{init/prompts.js → domains/setup/setup-prompts.js} +0 -0
- /package/dist/{tdd → domains/tdd}/runtime-context.js +0 -0
- /package/dist/{tdd → domains/tdd}/server-registry.js +0 -0
- /package/dist/{commands/tdd-daemon-process.js → domains/tdd/tdd-daemon-process.commands.js} +0 -0
- /package/dist/{types/index.d.ts → index.d.ts} +0 -0
- /package/dist/{utils → shared}/api-url.js +0 -0
- /package/dist/{utils → shared}/async-utils.js +0 -0
- /package/dist/{utils → shared}/browser.js +0 -0
- /package/dist/{utils → shared}/ci-env.js +0 -0
- /package/dist/{utils → shared}/colors.js +0 -0
- /package/dist/{utils → shared}/fetch-utils.js +0 -0
- /package/dist/{utils → shared}/git.js +0 -0
- /package/dist/{utils → shared}/patterns.js +0 -0
- /package/dist/{utils → shared}/secret-store.js +0 -0
- /package/dist/{errors → shared}/vizzly-error.js +0 -0
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Config Service
|
|
3
|
-
* Manages configuration for the TDD dashboard settings page
|
|
4
|
-
*
|
|
5
|
-
* Provides read/write access to:
|
|
6
|
-
* - Merged config (read-only combination of all sources)
|
|
7
|
-
* - Project config (vizzly.config.js in working directory)
|
|
8
|
-
* - Global config (~/.vizzly/config.json)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { existsSync } from 'node:fs';
|
|
12
|
-
import { writeFile } from 'node:fs/promises';
|
|
13
|
-
import { join } from 'node:path';
|
|
14
|
-
import { cosmiconfigSync } from 'cosmiconfig';
|
|
15
|
-
import { CONFIG_DEFAULTS } from '../config/core.js';
|
|
16
|
-
import { loadGlobalConfig, saveGlobalConfig } from '../utils/global-config.js';
|
|
17
|
-
import * as output from '../utils/output.js';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Create a config service instance
|
|
21
|
-
* @param {Object} options
|
|
22
|
-
* @param {string} options.workingDir - Working directory for project config
|
|
23
|
-
* @returns {Object} Config service with getConfig, updateConfig, validateConfig methods
|
|
24
|
-
*/
|
|
25
|
-
export function createConfigService({
|
|
26
|
-
workingDir
|
|
27
|
-
}) {
|
|
28
|
-
let projectConfigPath = null;
|
|
29
|
-
let projectConfigFormat = 'js'; // 'js' or 'json'
|
|
30
|
-
|
|
31
|
-
// Find project config file
|
|
32
|
-
let explorer = cosmiconfigSync('vizzly');
|
|
33
|
-
let searchResult = explorer.search(workingDir);
|
|
34
|
-
if (searchResult?.filepath) {
|
|
35
|
-
projectConfigPath = searchResult.filepath;
|
|
36
|
-
projectConfigFormat = searchResult.filepath.endsWith('.json') ? 'json' : 'js';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Get configuration by type
|
|
41
|
-
* @param {'merged'|'project'|'global'} type
|
|
42
|
-
* @returns {Promise<Object>}
|
|
43
|
-
*/
|
|
44
|
-
async function getConfig(type) {
|
|
45
|
-
if (type === 'merged') {
|
|
46
|
-
return getMergedConfig();
|
|
47
|
-
} else if (type === 'project') {
|
|
48
|
-
return getProjectConfig();
|
|
49
|
-
} else if (type === 'global') {
|
|
50
|
-
return getGlobalConfigData();
|
|
51
|
-
}
|
|
52
|
-
throw new Error(`Unknown config type: ${type}`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Get merged configuration with source tracking
|
|
57
|
-
*/
|
|
58
|
-
async function getMergedConfig() {
|
|
59
|
-
let config = cloneConfigValue(CONFIG_DEFAULTS);
|
|
60
|
-
let sources = {};
|
|
61
|
-
|
|
62
|
-
// Layer 1: Global config
|
|
63
|
-
let globalConfig = await loadGlobalConfig();
|
|
64
|
-
if (globalConfig.settings) {
|
|
65
|
-
mergeWithTracking(config, globalConfig.settings, sources, 'global');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Layer 2: Project config
|
|
69
|
-
if (projectConfigPath && existsSync(projectConfigPath)) {
|
|
70
|
-
try {
|
|
71
|
-
let result = explorer.load(projectConfigPath);
|
|
72
|
-
let projectConfig = result?.config?.default || result?.config || {};
|
|
73
|
-
mergeWithTracking(config, projectConfig, sources, 'project');
|
|
74
|
-
} catch (error) {
|
|
75
|
-
output.debug('config-service', `Error loading project config: ${error.message}`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Layer 3: Environment variables
|
|
80
|
-
if (process.env.VIZZLY_THRESHOLD) {
|
|
81
|
-
config.comparison.threshold = Number(process.env.VIZZLY_THRESHOLD);
|
|
82
|
-
sources.comparison = 'env';
|
|
83
|
-
}
|
|
84
|
-
if (process.env.VIZZLY_MIN_CLUSTER_SIZE) {
|
|
85
|
-
config.comparison.minClusterSize = Number(process.env.VIZZLY_MIN_CLUSTER_SIZE);
|
|
86
|
-
sources.comparison = 'env';
|
|
87
|
-
}
|
|
88
|
-
if (process.env.VIZZLY_PORT) {
|
|
89
|
-
config.server.port = Number(process.env.VIZZLY_PORT);
|
|
90
|
-
sources.server = 'env';
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
config,
|
|
94
|
-
sources
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Get project-level configuration only
|
|
100
|
-
*/
|
|
101
|
-
async function getProjectConfig() {
|
|
102
|
-
if (!projectConfigPath || !existsSync(projectConfigPath)) {
|
|
103
|
-
return {
|
|
104
|
-
config: {},
|
|
105
|
-
path: null
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
let result = explorer.load(projectConfigPath);
|
|
110
|
-
let config = result?.config?.default || result?.config || {};
|
|
111
|
-
return {
|
|
112
|
-
config,
|
|
113
|
-
path: projectConfigPath
|
|
114
|
-
};
|
|
115
|
-
} catch (error) {
|
|
116
|
-
output.debug('config-service', `Error loading project config: ${error.message}`);
|
|
117
|
-
return {
|
|
118
|
-
config: {},
|
|
119
|
-
path: projectConfigPath,
|
|
120
|
-
error: error.message
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Get global configuration only
|
|
127
|
-
*/
|
|
128
|
-
async function getGlobalConfigData() {
|
|
129
|
-
let globalConfig = await loadGlobalConfig();
|
|
130
|
-
return {
|
|
131
|
-
config: globalConfig.settings || {},
|
|
132
|
-
path: join(process.env.VIZZLY_HOME || join(process.env.HOME || '', '.vizzly'), 'config.json')
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Update configuration by type
|
|
138
|
-
* @param {'project'|'global'} type
|
|
139
|
-
* @param {Object} updates - Config updates to apply
|
|
140
|
-
* @returns {Promise<Object>}
|
|
141
|
-
*/
|
|
142
|
-
async function updateConfig(type, updates) {
|
|
143
|
-
if (type === 'project') {
|
|
144
|
-
return updateProjectConfig(updates);
|
|
145
|
-
} else if (type === 'global') {
|
|
146
|
-
return updateGlobalConfig(updates);
|
|
147
|
-
}
|
|
148
|
-
throw new Error(`Cannot update config type: ${type}`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Update project configuration (vizzly.config.js)
|
|
153
|
-
*/
|
|
154
|
-
async function updateProjectConfig(updates) {
|
|
155
|
-
// If no project config exists, create one
|
|
156
|
-
if (!projectConfigPath) {
|
|
157
|
-
projectConfigPath = join(workingDir, 'vizzly.config.js');
|
|
158
|
-
projectConfigFormat = 'js';
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Read existing config
|
|
162
|
-
let existingConfig = {};
|
|
163
|
-
if (existsSync(projectConfigPath)) {
|
|
164
|
-
try {
|
|
165
|
-
let result = explorer.load(projectConfigPath);
|
|
166
|
-
existingConfig = result?.config?.default || result?.config || {};
|
|
167
|
-
} catch {
|
|
168
|
-
// Start fresh if corrupted
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Merge updates
|
|
173
|
-
let newConfig = mergeDeep(existingConfig, updates);
|
|
174
|
-
|
|
175
|
-
// Write based on format
|
|
176
|
-
if (projectConfigFormat === 'json') {
|
|
177
|
-
await writeFile(projectConfigPath, JSON.stringify(newConfig, null, 2));
|
|
178
|
-
} else {
|
|
179
|
-
// Write as ES module
|
|
180
|
-
let content = `import { defineConfig } from '@vizzly-testing/cli/config';
|
|
181
|
-
|
|
182
|
-
export default defineConfig(${JSON.stringify(newConfig, null, 2)});
|
|
183
|
-
`;
|
|
184
|
-
await writeFile(projectConfigPath, content);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Clear cosmiconfig cache so next read gets fresh data
|
|
188
|
-
explorer.clearCaches();
|
|
189
|
-
return {
|
|
190
|
-
success: true,
|
|
191
|
-
path: projectConfigPath
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Update global configuration (~/.vizzly/config.json)
|
|
197
|
-
*/
|
|
198
|
-
async function updateGlobalConfig(updates) {
|
|
199
|
-
let globalConfig = await loadGlobalConfig();
|
|
200
|
-
if (!globalConfig.settings) {
|
|
201
|
-
globalConfig.settings = {};
|
|
202
|
-
}
|
|
203
|
-
globalConfig.settings = mergeDeep(globalConfig.settings, updates);
|
|
204
|
-
await saveGlobalConfig(globalConfig);
|
|
205
|
-
return {
|
|
206
|
-
success: true
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Validate configuration
|
|
212
|
-
* @param {Object} config - Config to validate
|
|
213
|
-
* @returns {Promise<Object>}
|
|
214
|
-
*/
|
|
215
|
-
async function validateConfig(config) {
|
|
216
|
-
let errors = [];
|
|
217
|
-
let warnings = [];
|
|
218
|
-
|
|
219
|
-
// Validate threshold
|
|
220
|
-
if (config.comparison?.threshold !== undefined) {
|
|
221
|
-
let threshold = config.comparison.threshold;
|
|
222
|
-
if (typeof threshold !== 'number' || !Number.isFinite(threshold) || threshold < 0) {
|
|
223
|
-
errors.push('comparison.threshold must be a non-negative number');
|
|
224
|
-
} else if (threshold > 100) {
|
|
225
|
-
warnings.push('comparison.threshold above 100 may cause all comparisons to pass');
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Validate minClusterSize
|
|
230
|
-
if (config.comparison?.minClusterSize !== undefined) {
|
|
231
|
-
let minClusterSize = config.comparison.minClusterSize;
|
|
232
|
-
if (!Number.isInteger(minClusterSize) || minClusterSize < 1) {
|
|
233
|
-
errors.push('comparison.minClusterSize must be a positive integer (1 or greater)');
|
|
234
|
-
} else if (minClusterSize > 100) {
|
|
235
|
-
warnings.push('comparison.minClusterSize above 100 may filter out most differences');
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Validate port
|
|
240
|
-
if (config.server?.port !== undefined) {
|
|
241
|
-
let port = config.server.port;
|
|
242
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
243
|
-
errors.push('server.port must be an integer between 1 and 65535');
|
|
244
|
-
} else if (port < 1024) {
|
|
245
|
-
warnings.push('server.port below 1024 may require elevated privileges');
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Validate timeout
|
|
250
|
-
if (config.server?.timeout !== undefined) {
|
|
251
|
-
let timeout = config.server.timeout;
|
|
252
|
-
if (!Number.isInteger(timeout) || timeout < 0) {
|
|
253
|
-
errors.push('server.timeout must be a non-negative integer');
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
valid: errors.length === 0,
|
|
258
|
-
errors,
|
|
259
|
-
warnings
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
return {
|
|
263
|
-
getConfig,
|
|
264
|
-
updateConfig,
|
|
265
|
-
validateConfig
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Deep merge two objects
|
|
271
|
-
*/
|
|
272
|
-
function mergeDeep(target, source) {
|
|
273
|
-
let result = cloneConfigValue(target);
|
|
274
|
-
for (let key in source) {
|
|
275
|
-
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
276
|
-
result[key] = mergeDeep(result[key] || {}, source[key]);
|
|
277
|
-
} else if (Array.isArray(source[key])) {
|
|
278
|
-
result[key] = [...source[key]];
|
|
279
|
-
} else {
|
|
280
|
-
result[key] = source[key];
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
return result;
|
|
284
|
-
}
|
|
285
|
-
function cloneConfigValue(value) {
|
|
286
|
-
if (Array.isArray(value)) {
|
|
287
|
-
return value.map(cloneConfigValue);
|
|
288
|
-
}
|
|
289
|
-
if (value && typeof value === 'object') {
|
|
290
|
-
let clone = {};
|
|
291
|
-
for (let key in value) {
|
|
292
|
-
clone[key] = cloneConfigValue(value[key]);
|
|
293
|
-
}
|
|
294
|
-
return clone;
|
|
295
|
-
}
|
|
296
|
-
return value;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Merge config with source tracking
|
|
301
|
-
*/
|
|
302
|
-
function mergeWithTracking(target, source, sources, sourceName, sectionName) {
|
|
303
|
-
for (let key in source) {
|
|
304
|
-
let sourceSection = sectionName || key;
|
|
305
|
-
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
306
|
-
if (!target[key]) target[key] = {};
|
|
307
|
-
mergeWithTracking(target[key], source[key], sources, sourceName, sourceSection);
|
|
308
|
-
} else {
|
|
309
|
-
target[key] = source[key];
|
|
310
|
-
sources[sourceSection] = sourceName;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project Service
|
|
3
|
-
* Wraps project operations for use by the HTTP server
|
|
4
|
-
*
|
|
5
|
-
* Provides the interface expected by src/server/routers/projects.js:
|
|
6
|
-
* - listProjects() - Returns [] if not authenticated
|
|
7
|
-
* - getRecentBuilds(projectSlug, organizationSlug, options) - Returns [] if not authenticated
|
|
8
|
-
*
|
|
9
|
-
* Error handling:
|
|
10
|
-
* - API methods (listProjects, getRecentBuilds) return empty arrays when not authenticated
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { createAuthClient } from '../auth/client.js';
|
|
14
|
-
import * as projectOps from '../project/operations.js';
|
|
15
|
-
import { getApiUrl } from '../utils/environment-config.js';
|
|
16
|
-
import { getAuthTokens } from '../utils/global-config.js';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Create a project service instance
|
|
20
|
-
* @param {Object} [options]
|
|
21
|
-
* @param {string} [options.apiUrl] - API base URL (defaults to VIZZLY_API_URL or https://app.vizzly.dev)
|
|
22
|
-
* @param {Object} [options.httpClient] - Injectable HTTP client (for testing)
|
|
23
|
-
* @param {Function} [options.getAuthTokens] - Injectable token getter (for testing)
|
|
24
|
-
* @returns {Object} Project service
|
|
25
|
-
*/
|
|
26
|
-
export function createProjectService(options = {}) {
|
|
27
|
-
let apiUrl = options.apiUrl || getApiUrl();
|
|
28
|
-
|
|
29
|
-
// Create HTTP client once at service creation (not per-request)
|
|
30
|
-
// Allow injection for testing
|
|
31
|
-
let httpClient = options.httpClient || createAuthClient({
|
|
32
|
-
baseUrl: apiUrl
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// Allow injection of getAuthTokens for testing
|
|
36
|
-
let tokenGetter = options.getAuthTokens || getAuthTokens;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Create an OAuth client with current access token
|
|
40
|
-
* @returns {Promise<Object|null>} OAuth client or null if not authenticated
|
|
41
|
-
*/
|
|
42
|
-
async function createOAuthClient() {
|
|
43
|
-
let auth = await tokenGetter();
|
|
44
|
-
if (!auth?.accessToken) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Wrap authenticatedRequest to auto-inject the access token
|
|
49
|
-
return {
|
|
50
|
-
authenticatedRequest: (endpoint, fetchOptions = {}) => httpClient.authenticatedRequest(endpoint, auth.accessToken, fetchOptions)
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
/**
|
|
55
|
-
* List all projects from API
|
|
56
|
-
* Returns empty array if not authenticated (projectOps handles null oauthClient)
|
|
57
|
-
* @returns {Promise<Array>} Array of projects, empty if not authenticated
|
|
58
|
-
*/
|
|
59
|
-
async listProjects() {
|
|
60
|
-
let oauthClient = await createOAuthClient();
|
|
61
|
-
// projectOps.listProjects handles null oauthClient by returning []
|
|
62
|
-
return projectOps.listProjects({
|
|
63
|
-
oauthClient,
|
|
64
|
-
apiClient: null
|
|
65
|
-
});
|
|
66
|
-
},
|
|
67
|
-
/**
|
|
68
|
-
* Get recent builds for a project
|
|
69
|
-
* Returns empty array if not authenticated (projectOps handles null oauthClient)
|
|
70
|
-
* @param {string} projectSlug - Project slug
|
|
71
|
-
* @param {string} organizationSlug - Organization slug
|
|
72
|
-
* @param {Object} options - Query options
|
|
73
|
-
* @returns {Promise<Array>} Array of builds, empty if not authenticated
|
|
74
|
-
*/
|
|
75
|
-
async getRecentBuilds(projectSlug, organizationSlug, options = {}) {
|
|
76
|
-
let oauthClient = await createOAuthClient();
|
|
77
|
-
// projectOps.getRecentBuilds handles null oauthClient by returning []
|
|
78
|
-
return projectOps.getRecentBuilds({
|
|
79
|
-
oauthClient,
|
|
80
|
-
apiClient: null,
|
|
81
|
-
projectSlug,
|
|
82
|
-
organizationSlug,
|
|
83
|
-
limit: options.limit,
|
|
84
|
-
branch: options.branch
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}
|
package/dist/test-runner/core.js
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test Runner Core - Pure functions for test execution logic
|
|
3
|
-
*
|
|
4
|
-
* No I/O, no side effects - just data transformations.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// ============================================================================
|
|
8
|
-
// Environment Building
|
|
9
|
-
// ============================================================================
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Build environment variables for test process execution
|
|
13
|
-
* @param {Object} options - Options
|
|
14
|
-
* @param {number} options.port - Server port
|
|
15
|
-
* @param {string} options.buildId - Build ID
|
|
16
|
-
* @param {boolean} [options.setBaseline] - Whether to set baseline
|
|
17
|
-
* @param {boolean} [options.failOnDiff] - Whether visual diffs should fail tests
|
|
18
|
-
* @param {Object} [options.baseEnv] - Base environment (defaults to process.env)
|
|
19
|
-
* @returns {Object} Environment variables object
|
|
20
|
-
*/
|
|
21
|
-
export function buildTestEnv({
|
|
22
|
-
port,
|
|
23
|
-
buildId,
|
|
24
|
-
setBaseline = false,
|
|
25
|
-
failOnDiff = false,
|
|
26
|
-
baseEnv = process.env
|
|
27
|
-
}) {
|
|
28
|
-
return {
|
|
29
|
-
...baseEnv,
|
|
30
|
-
VIZZLY_SERVER_URL: `http://localhost:${port}`,
|
|
31
|
-
VIZZLY_BUILD_ID: buildId,
|
|
32
|
-
VIZZLY_ENABLED: 'true',
|
|
33
|
-
VIZZLY_SET_BASELINE: setBaseline ? 'true' : 'false',
|
|
34
|
-
VIZZLY_FAIL_ON_DIFF: failOnDiff ? 'true' : 'false'
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Build environment for disabled Vizzly (allowNoToken mode)
|
|
40
|
-
* @param {Object} [baseEnv] - Base environment (defaults to process.env)
|
|
41
|
-
* @returns {Object} Environment variables object
|
|
42
|
-
*/
|
|
43
|
-
export function buildDisabledEnv(baseEnv = process.env) {
|
|
44
|
-
return {
|
|
45
|
-
...baseEnv,
|
|
46
|
-
VIZZLY_ENABLED: 'false'
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// ============================================================================
|
|
51
|
-
// Build Payload Building
|
|
52
|
-
// ============================================================================
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Build API build payload from options
|
|
56
|
-
* @param {Object} options - Run options
|
|
57
|
-
* @param {Object} [comparisonConfig] - Comparison configuration (threshold, minClusterSize)
|
|
58
|
-
* @returns {Object} Build payload for API
|
|
59
|
-
*/
|
|
60
|
-
export function buildApiBuildPayload(options, comparisonConfig = null) {
|
|
61
|
-
let payload = {
|
|
62
|
-
name: options.buildName || `Test Run ${new Date().toISOString()}`,
|
|
63
|
-
branch: options.branch || 'main',
|
|
64
|
-
environment: options.environment || 'test',
|
|
65
|
-
commit_sha: options.commit,
|
|
66
|
-
commit_message: options.message,
|
|
67
|
-
commit_author_name: options.commitAuthorName,
|
|
68
|
-
commit_author_email: options.commitAuthorEmail,
|
|
69
|
-
github_pull_request_number: options.pullRequestNumber,
|
|
70
|
-
parallel_id: options.parallelId
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// Only include metadata if we have meaningful config to send
|
|
74
|
-
if (comparisonConfig?.threshold != null || comparisonConfig?.minClusterSize != null) {
|
|
75
|
-
payload.metadata = {
|
|
76
|
-
comparison: {
|
|
77
|
-
threshold: comparisonConfig.threshold,
|
|
78
|
-
minClusterSize: comparisonConfig.minClusterSize
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
return payload;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ============================================================================
|
|
86
|
-
// Mode Determination
|
|
87
|
-
// ============================================================================
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Determine if we should skip Vizzly integration entirely
|
|
91
|
-
* @param {Object} options - Options
|
|
92
|
-
* @param {boolean} options.allowNoToken - Whether to allow running without token
|
|
93
|
-
* @param {boolean} options.hasApiKey - Whether an API key is available
|
|
94
|
-
* @param {boolean} options.tdd - Whether in TDD mode
|
|
95
|
-
* @returns {boolean} True if Vizzly should be disabled
|
|
96
|
-
*/
|
|
97
|
-
export function shouldDisableVizzly({
|
|
98
|
-
allowNoToken,
|
|
99
|
-
hasApiKey,
|
|
100
|
-
tdd
|
|
101
|
-
}) {
|
|
102
|
-
return allowNoToken && !hasApiKey && !tdd;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Determine build mode
|
|
107
|
-
* @param {boolean} tdd - Whether TDD mode is enabled
|
|
108
|
-
* @returns {'tdd'|'api'} Build mode
|
|
109
|
-
*/
|
|
110
|
-
export function determineBuildMode(tdd) {
|
|
111
|
-
return tdd ? 'tdd' : 'api';
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ============================================================================
|
|
115
|
-
// Result Building
|
|
116
|
-
// ============================================================================
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Build result object for disabled run (no Vizzly integration)
|
|
120
|
-
* @returns {Object} Result object
|
|
121
|
-
*/
|
|
122
|
-
export function buildDisabledRunResult(reason = null) {
|
|
123
|
-
return {
|
|
124
|
-
testsPassed: 1,
|
|
125
|
-
testsFailed: 0,
|
|
126
|
-
screenshotsCaptured: 0,
|
|
127
|
-
...(reason ? {
|
|
128
|
-
capture: {
|
|
129
|
-
setup: 'disabled',
|
|
130
|
-
reason,
|
|
131
|
-
accepted: 0,
|
|
132
|
-
rejected: 0,
|
|
133
|
-
errors: 0
|
|
134
|
-
}
|
|
135
|
-
} : {})
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Build final run result object
|
|
141
|
-
* @param {Object} options - Options
|
|
142
|
-
* @param {string|null} options.buildId - Build ID
|
|
143
|
-
* @param {string|null} options.buildUrl - Build URL
|
|
144
|
-
* @param {boolean} options.testSuccess - Whether tests passed
|
|
145
|
-
* @param {number} options.screenshotCount - Number of screenshots
|
|
146
|
-
* @param {Object|null} options.tddResults - TDD results (comparisons, etc.)
|
|
147
|
-
* @param {Object|null} [options.uploadStats] - Cumulative cloud upload outcomes,
|
|
148
|
-
* exposed as `uploads` independently of test and local comparison results.
|
|
149
|
-
* @returns {Object} Final result object
|
|
150
|
-
*/
|
|
151
|
-
export function buildRunResult({
|
|
152
|
-
buildId,
|
|
153
|
-
buildUrl,
|
|
154
|
-
testSuccess,
|
|
155
|
-
screenshotCount,
|
|
156
|
-
tddResults,
|
|
157
|
-
uploadStats
|
|
158
|
-
}) {
|
|
159
|
-
return {
|
|
160
|
-
buildId,
|
|
161
|
-
url: buildUrl,
|
|
162
|
-
testsPassed: testSuccess ? 1 : 0,
|
|
163
|
-
testsFailed: testSuccess ? 0 : 1,
|
|
164
|
-
screenshotsCaptured: screenshotCount,
|
|
165
|
-
...(uploadStats ? {
|
|
166
|
-
uploads: uploadStats
|
|
167
|
-
} : {}),
|
|
168
|
-
comparisons: tddResults?.comparisons || null,
|
|
169
|
-
failed: (tddResults?.failed || 0) > 0,
|
|
170
|
-
...(tddResults ? {
|
|
171
|
-
summary: {
|
|
172
|
-
total: tddResults.total ?? screenshotCount,
|
|
173
|
-
passed: tddResults.passed || 0,
|
|
174
|
-
failed: tddResults.failed || 0,
|
|
175
|
-
new: tddResults.new || 0,
|
|
176
|
-
errors: tddResults.errors || 0
|
|
177
|
-
},
|
|
178
|
-
capture: {
|
|
179
|
-
setup: 'ready',
|
|
180
|
-
reason: tddResults.error || null,
|
|
181
|
-
accepted: tddResults.capture?.accepted ?? screenshotCount,
|
|
182
|
-
rejected: tddResults.capture?.rejected || 0,
|
|
183
|
-
errors: tddResults.errors || 0
|
|
184
|
-
}
|
|
185
|
-
} : {})
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ============================================================================
|
|
190
|
-
// Validation
|
|
191
|
-
// ============================================================================
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Validate test command is provided
|
|
195
|
-
* @param {string|undefined} testCommand - Test command to validate
|
|
196
|
-
* @returns {{ valid: boolean, error: string|null }}
|
|
197
|
-
*/
|
|
198
|
-
export function validateTestCommand(testCommand) {
|
|
199
|
-
if (!testCommand) {
|
|
200
|
-
return {
|
|
201
|
-
valid: false,
|
|
202
|
-
error: 'No test command provided'
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
valid: true,
|
|
207
|
-
error: null
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Validate daemon mode requirements
|
|
213
|
-
* @param {Object} options - Options
|
|
214
|
-
* @param {boolean} options.tdd - Whether TDD mode is enabled
|
|
215
|
-
* @param {boolean} options.daemon - Whether daemon mode is enabled
|
|
216
|
-
* @returns {{ valid: boolean, error: string|null }}
|
|
217
|
-
*/
|
|
218
|
-
export function validateDaemonMode({
|
|
219
|
-
tdd,
|
|
220
|
-
daemon
|
|
221
|
-
}) {
|
|
222
|
-
if (!tdd || !daemon) {
|
|
223
|
-
return {
|
|
224
|
-
valid: false,
|
|
225
|
-
error: 'Initialize method is only for TDD daemon mode'
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
return {
|
|
229
|
-
valid: true,
|
|
230
|
-
error: null
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// ============================================================================
|
|
235
|
-
// Client Options Building
|
|
236
|
-
// ============================================================================
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Build API client options from config
|
|
240
|
-
* @param {Object} config - Configuration object
|
|
241
|
-
* @returns {Object|null} Client options or null if no API key
|
|
242
|
-
*/
|
|
243
|
-
export function buildClientOptions(config) {
|
|
244
|
-
if (!config?.apiKey) {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
return {
|
|
248
|
-
baseUrl: config.apiUrl,
|
|
249
|
-
token: config.apiKey,
|
|
250
|
-
command: 'run'
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Check if API key is available
|
|
256
|
-
* @param {Object} config - Configuration object
|
|
257
|
-
* @returns {boolean} Whether API key exists
|
|
258
|
-
*/
|
|
259
|
-
export function hasApiKey(config) {
|
|
260
|
-
return Boolean(config?.apiKey);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// ============================================================================
|
|
264
|
-
// Spawn Options Building
|
|
265
|
-
// ============================================================================
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Build spawn options for test command execution
|
|
269
|
-
* @param {Object} env - Environment variables
|
|
270
|
-
* @param {Object} [options] - Spawn display options
|
|
271
|
-
* @param {boolean} [options.json] - Keep stdout reserved for CLI JSON output
|
|
272
|
-
* @returns {Object} Spawn options
|
|
273
|
-
*/
|
|
274
|
-
export function buildSpawnOptions(env, options = {}) {
|
|
275
|
-
return {
|
|
276
|
-
env,
|
|
277
|
-
stdio: options.json ? ['inherit', 'pipe', 'pipe'] : 'inherit',
|
|
278
|
-
shell: true
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// ============================================================================
|
|
283
|
-
// Set Baseline Normalization
|
|
284
|
-
// ============================================================================
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Normalize setBaseline option (handles both camelCase and kebab-case)
|
|
288
|
-
* @param {Object} options - Options object
|
|
289
|
-
* @returns {boolean} Whether to set baseline
|
|
290
|
-
*/
|
|
291
|
-
export function normalizeSetBaseline(options) {
|
|
292
|
-
return Boolean(options?.setBaseline || options?.['set-baseline']);
|
|
293
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test Runner Module - Public exports
|
|
3
|
-
*
|
|
4
|
-
* Provides functional test runner primitives:
|
|
5
|
-
* - core.js: Pure functions for building env, payloads, results
|
|
6
|
-
* - operations.js: Test execution operations with dependency injection
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// Core pure functions
|
|
10
|
-
export { buildApiBuildPayload, buildClientOptions, buildDisabledEnv, buildDisabledRunResult, buildRunResult, buildSpawnOptions, buildTestEnv, determineBuildMode, hasApiKey, normalizeSetBaseline, shouldDisableVizzly, validateDaemonMode, validateTestCommand } from './core.js';
|
|
11
|
-
|
|
12
|
-
// Test runner operations (take dependencies as parameters)
|
|
13
|
-
export { cancelTests, createBuild, executeTestCommand, fetchBuildUrl, finalizeBuild, initializeDaemon, runTests } from './operations.js';
|