@vizzly-testing/cli 0.34.0 → 0.35.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 +39 -3
- package/dist/api/core.js +2 -1
- package/dist/cli.js +19 -6
- package/dist/client/index.js +62 -21
- package/dist/commands/api.js +9 -3
- package/dist/commands/preview.js +13 -11
- package/dist/commands/project.js +1 -1
- package/dist/commands/run.js +18 -6
- package/dist/commands/tdd-daemon.js +8 -3
- package/dist/commands/tdd.js +24 -4
- package/dist/commands/upload.js +31 -13
- package/dist/reporter/reporter-bundle.iife.js +1 -1
- package/dist/sdk/index.js +23 -9
- package/dist/server/handlers/api-handler.js +3 -2
- package/dist/server/handlers/tdd-handler.js +5 -1
- package/dist/server/routers/screenshot.js +11 -4
- package/dist/services/build-manager.js +6 -1
- package/dist/test-runner/core.js +4 -1
- package/dist/test-runner/operations.js +2 -1
- package/dist/types/client.d.ts +32 -5
- package/dist/types/index.d.ts +33 -5
- package/dist/uploader/core.js +41 -9
- package/dist/uploader/operations.js +19 -17
- package/dist/utils/config-loader.js +12 -6
- package/dist/utils/context.js +46 -76
- package/dist/utils/environment-config.js +20 -0
- package/dist/utils/output.js +5 -5
- package/dist/utils/screenshot-options.js +60 -10
- package/package.json +1 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -24,7 +24,9 @@ export interface BuildConfig {
|
|
|
24
24
|
|
|
25
25
|
export interface UploadConfig {
|
|
26
26
|
screenshotsDir?: string | string[];
|
|
27
|
+
/** Batch size used when uploading screenshots. */
|
|
27
28
|
batchSize?: number;
|
|
29
|
+
/** Timeout in milliseconds for waiting on build processing after upload. */
|
|
28
30
|
timeout?: number;
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -76,13 +78,22 @@ export interface ScreenshotOptions {
|
|
|
76
78
|
threshold?: number;
|
|
77
79
|
minClusterSize?: number;
|
|
78
80
|
fullPage?: boolean;
|
|
81
|
+
/** Transport-only build ID used to route the request. */
|
|
79
82
|
buildId?: string;
|
|
80
|
-
|
|
83
|
+
/** Client-side HTTP timeout in milliseconds; not stored as metadata. */
|
|
84
|
+
requestTimeout?: number;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
export interface ScreenshotResult {
|
|
84
88
|
success: boolean;
|
|
85
|
-
status?:
|
|
89
|
+
status?:
|
|
90
|
+
| 'passed'
|
|
91
|
+
| 'failed'
|
|
92
|
+
| 'new'
|
|
93
|
+
| 'match'
|
|
94
|
+
| 'diff'
|
|
95
|
+
| 'baseline-updated'
|
|
96
|
+
| 'error';
|
|
86
97
|
name?: string;
|
|
87
98
|
diffPercentage?: number;
|
|
88
99
|
[key: string]: unknown;
|
|
@@ -95,7 +106,13 @@ export interface ScreenshotResult {
|
|
|
95
106
|
export interface ComparisonResult {
|
|
96
107
|
id: string;
|
|
97
108
|
name: string;
|
|
98
|
-
status:
|
|
109
|
+
status:
|
|
110
|
+
| 'passed'
|
|
111
|
+
| 'failed'
|
|
112
|
+
| 'new'
|
|
113
|
+
| 'error'
|
|
114
|
+
| 'baseline-created'
|
|
115
|
+
| 'baseline-updated';
|
|
99
116
|
baseline: string;
|
|
100
117
|
current: string;
|
|
101
118
|
diff: string | null;
|
|
@@ -150,7 +167,9 @@ export interface UploadOptions {
|
|
|
150
167
|
message?: string;
|
|
151
168
|
environment?: string;
|
|
152
169
|
threshold?: number;
|
|
153
|
-
|
|
170
|
+
minClusterSize?: number;
|
|
171
|
+
metadata?: Record<string, unknown>;
|
|
172
|
+
pullRequestNumber?: string | number;
|
|
154
173
|
parallelId?: string;
|
|
155
174
|
onProgress?: (progress: UploadProgress) => void;
|
|
156
175
|
}
|
|
@@ -186,8 +205,10 @@ export interface BuildResult {
|
|
|
186
205
|
status: 'completed' | 'failed' | 'pending';
|
|
187
206
|
build: unknown;
|
|
188
207
|
comparisons?: number;
|
|
208
|
+
totalComparisons: number;
|
|
189
209
|
passedComparisons?: number;
|
|
190
210
|
failedComparisons?: number;
|
|
211
|
+
newComparisons: number;
|
|
191
212
|
url?: string;
|
|
192
213
|
}
|
|
193
214
|
|
|
@@ -487,15 +508,19 @@ export interface BuildOptions {
|
|
|
487
508
|
buildName?: string;
|
|
488
509
|
branch?: string;
|
|
489
510
|
commit?: string;
|
|
511
|
+
commit_sha?: string;
|
|
490
512
|
message?: string;
|
|
513
|
+
commit_message?: string;
|
|
491
514
|
environment?: string;
|
|
492
515
|
threshold?: number;
|
|
493
516
|
eager?: boolean;
|
|
494
517
|
allowNoToken?: boolean;
|
|
495
518
|
wait?: boolean;
|
|
496
519
|
uploadAll?: boolean;
|
|
497
|
-
pullRequestNumber?: string;
|
|
520
|
+
pullRequestNumber?: string | number;
|
|
521
|
+
github_pull_request_number?: string | number;
|
|
498
522
|
parallelId?: string;
|
|
523
|
+
parallel_id?: string;
|
|
499
524
|
}
|
|
500
525
|
|
|
501
526
|
/**
|
|
@@ -565,6 +590,8 @@ export function vizzlyScreenshot(
|
|
|
565
590
|
threshold?: number;
|
|
566
591
|
minClusterSize?: number;
|
|
567
592
|
fullPage?: boolean;
|
|
593
|
+
requestTimeout?: number;
|
|
594
|
+
buildId?: string;
|
|
568
595
|
[key: string]: unknown;
|
|
569
596
|
}
|
|
570
597
|
): Promise<ScreenshotResult | null>;
|
|
@@ -573,6 +600,7 @@ export function vizzlyScreenshot(
|
|
|
573
600
|
export function configure(config?: {
|
|
574
601
|
serverUrl?: string;
|
|
575
602
|
enabled?: boolean;
|
|
603
|
+
failOnDiff?: boolean;
|
|
576
604
|
}): void;
|
|
577
605
|
|
|
578
606
|
/** Enable or disable screenshot capture */
|
package/dist/uploader/core.js
CHANGED
|
@@ -123,6 +123,25 @@ export function extractBrowserFromFilename(filename) {
|
|
|
123
123
|
// Build Info Construction
|
|
124
124
|
// ============================================================================
|
|
125
125
|
|
|
126
|
+
function buildUploadMetadata(options) {
|
|
127
|
+
let metadata = {
|
|
128
|
+
...(options.metadata || {})
|
|
129
|
+
};
|
|
130
|
+
let comparison = {
|
|
131
|
+
...(metadata.comparison || {})
|
|
132
|
+
};
|
|
133
|
+
if (options.threshold !== undefined) {
|
|
134
|
+
comparison.threshold = options.threshold;
|
|
135
|
+
}
|
|
136
|
+
if (options.minClusterSize !== undefined) {
|
|
137
|
+
comparison.minClusterSize = options.minClusterSize;
|
|
138
|
+
}
|
|
139
|
+
if (Object.keys(comparison).length > 0) {
|
|
140
|
+
metadata.comparison = comparison;
|
|
141
|
+
}
|
|
142
|
+
return Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
126
145
|
/**
|
|
127
146
|
* Build API build info payload
|
|
128
147
|
* @param {Object} options - Upload options
|
|
@@ -130,7 +149,7 @@ export function extractBrowserFromFilename(filename) {
|
|
|
130
149
|
* @returns {Object} Build info payload
|
|
131
150
|
*/
|
|
132
151
|
export function buildBuildInfo(options, defaultBranch = 'main') {
|
|
133
|
-
|
|
152
|
+
let buildInfo = {
|
|
134
153
|
name: options.buildName || `Upload ${new Date().toISOString()}`,
|
|
135
154
|
branch: options.branch || defaultBranch || 'main',
|
|
136
155
|
commit_sha: options.commit,
|
|
@@ -140,6 +159,11 @@ export function buildBuildInfo(options, defaultBranch = 'main') {
|
|
|
140
159
|
github_pull_request_number: options.pullRequestNumber,
|
|
141
160
|
parallel_id: options.parallelId
|
|
142
161
|
};
|
|
162
|
+
let metadata = buildUploadMetadata(options);
|
|
163
|
+
if (metadata) {
|
|
164
|
+
buildInfo.metadata = metadata;
|
|
165
|
+
}
|
|
166
|
+
return buildInfo;
|
|
143
167
|
}
|
|
144
168
|
|
|
145
169
|
// ============================================================================
|
|
@@ -316,17 +340,25 @@ export function buildUploadResult({
|
|
|
316
340
|
* @returns {Object} Wait result
|
|
317
341
|
*/
|
|
318
342
|
export function buildWaitResult(build) {
|
|
343
|
+
let totalComparisons = build.comparisonsTotal ?? build.total_comparisons ?? 0;
|
|
344
|
+
let passedComparisons = build.comparisonsPassed ?? build.passed_comparisons ?? build.identicalComparisons ?? build.identical_comparisons ?? 0;
|
|
345
|
+
let failedComparisons = build.comparisonsFailed ?? build.failed_comparisons ?? build.changedComparisons ?? build.changed_comparisons ?? 0;
|
|
346
|
+
let newComparisons = build.comparisonsNew ?? build.newComparisons ?? build.new_comparisons ?? 0;
|
|
347
|
+
let identicalComparisons = build.identicalComparisons ?? build.identical_comparisons ?? passedComparisons;
|
|
348
|
+
let approvalStatus = build.approvalStatus ?? build.approval_status ?? 'pending';
|
|
349
|
+
let hasComparisonCounts = build.comparisonsTotal !== undefined || build.total_comparisons !== undefined || build.comparisonsPassed !== undefined || build.passed_comparisons !== undefined || build.comparisonsFailed !== undefined || build.failed_comparisons !== undefined || build.comparisonsNew !== undefined || build.new_comparisons !== undefined || build.identicalComparisons !== undefined || build.identical_comparisons !== undefined;
|
|
319
350
|
let result = {
|
|
320
351
|
status: 'completed',
|
|
321
|
-
build
|
|
352
|
+
build,
|
|
353
|
+
passedComparisons,
|
|
354
|
+
failedComparisons,
|
|
355
|
+
newComparisons,
|
|
356
|
+
identicalComparisons,
|
|
357
|
+
approvalStatus,
|
|
358
|
+
totalComparisons
|
|
322
359
|
};
|
|
323
|
-
if (
|
|
324
|
-
result.comparisons =
|
|
325
|
-
result.passedComparisons = build.comparisonsPassed || 0;
|
|
326
|
-
result.failedComparisons = build.comparisonsFailed || 0;
|
|
327
|
-
} else {
|
|
328
|
-
result.passedComparisons = 0;
|
|
329
|
-
result.failedComparisons = 0;
|
|
360
|
+
if (hasComparisonCounts) {
|
|
361
|
+
result.comparisons = totalComparisons;
|
|
330
362
|
}
|
|
331
363
|
if (build.url) {
|
|
332
364
|
result.url = build.url;
|
|
@@ -386,23 +386,25 @@ export async function upload({
|
|
|
386
386
|
let buildInfo = buildBuildInfo(uploadOptions, defaultBranch);
|
|
387
387
|
let build = await createBuild(client, buildInfo);
|
|
388
388
|
let buildId = build.id;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
let
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
389
|
+
let toUpload = fileMetadata;
|
|
390
|
+
let existing = [];
|
|
391
|
+
let screenshots = [];
|
|
392
|
+
if (!uploadOptions.uploadAll) {
|
|
393
|
+
let existingResult = await checkExistingFiles({
|
|
394
|
+
fileMetadata,
|
|
395
|
+
client,
|
|
396
|
+
signal,
|
|
397
|
+
buildId,
|
|
398
|
+
deps: {
|
|
399
|
+
checkShas,
|
|
400
|
+
createError,
|
|
401
|
+
output
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
toUpload = existingResult.toUpload;
|
|
405
|
+
existing = existingResult.existing;
|
|
406
|
+
screenshots = existingResult.screenshots;
|
|
407
|
+
}
|
|
406
408
|
onProgress(buildDeduplicationProgress(toUpload.length, existing.length, files.length));
|
|
407
409
|
|
|
408
410
|
// Upload files
|
|
@@ -2,7 +2,7 @@ import { resolve } from 'node:path';
|
|
|
2
2
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
3
3
|
import { CONFIG_DEFAULTS, deepMerge } from '../config/core.js';
|
|
4
4
|
import { validateVizzlyConfigWithDefaults } from './config-schema.js';
|
|
5
|
-
import { getApiToken, getApiUrl, getBuildName, getParallelId } from './environment-config.js';
|
|
5
|
+
import { getApiToken, getApiUrl, getBuildName, getMinClusterSize, getParallelId, getThreshold } from './environment-config.js';
|
|
6
6
|
import { getAccessToken } from './global-config.js';
|
|
7
7
|
import * as output from './output.js';
|
|
8
8
|
import { getActiveProjectLink } from './project-link-store.js';
|
|
@@ -26,10 +26,12 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
26
26
|
mergeConfig(config, validatedFileConfig);
|
|
27
27
|
|
|
28
28
|
// 3. Override with environment variables (higher priority than fallbacks)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
let envApiKey = getApiToken();
|
|
30
|
+
let envApiUrl = getApiUrl();
|
|
31
|
+
let envBuildName = getBuildName();
|
|
32
|
+
let envParallelId = getParallelId();
|
|
33
|
+
let envThreshold = getThreshold();
|
|
34
|
+
let envMinClusterSize = getMinClusterSize();
|
|
33
35
|
if (envApiKey) {
|
|
34
36
|
config.apiKey = envApiKey;
|
|
35
37
|
output.debug('config', 'using token from environment');
|
|
@@ -40,6 +42,10 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
40
42
|
output.debug('config', 'using build name from environment');
|
|
41
43
|
}
|
|
42
44
|
if (envParallelId) config.parallelId = envParallelId;
|
|
45
|
+
if (envThreshold !== undefined) config.comparison.threshold = envThreshold;
|
|
46
|
+
if (envMinClusterSize !== undefined) {
|
|
47
|
+
config.comparison.minClusterSize = envMinClusterSize;
|
|
48
|
+
}
|
|
43
49
|
|
|
44
50
|
// 4. Apply CLI overrides (highest priority)
|
|
45
51
|
if (cliOverrides.token) {
|
|
@@ -109,7 +115,7 @@ function applyCLIOverrides(config, cliOverrides = {}) {
|
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
// Comparison overrides
|
|
112
|
-
if (cliOverrides.threshold !== undefined) config.comparison.threshold = cliOverrides.threshold;
|
|
118
|
+
if (cliOverrides.threshold !== undefined) config.comparison.threshold = Number(cliOverrides.threshold);
|
|
113
119
|
if (cliOverrides.minClusterSize !== undefined) {
|
|
114
120
|
config.comparison.minClusterSize = Number(cliOverrides.minClusterSize);
|
|
115
121
|
}
|
package/dist/utils/context.js
CHANGED
|
@@ -11,6 +11,37 @@
|
|
|
11
11
|
import { existsSync, readFileSync } from 'node:fs';
|
|
12
12
|
import { homedir } from 'node:os';
|
|
13
13
|
import { join } from 'node:path';
|
|
14
|
+
function getGlobalConfigPath() {
|
|
15
|
+
return join(process.env.VIZZLY_HOME || join(homedir(), '.vizzly'), 'config.json');
|
|
16
|
+
}
|
|
17
|
+
function readJsonFile(path) {
|
|
18
|
+
try {
|
|
19
|
+
if (existsSync(path)) {
|
|
20
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
// Optional context should never block the command that is rendering it.
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
function readGlobalConfig() {
|
|
28
|
+
return readJsonFile(getGlobalConfigPath()) || {};
|
|
29
|
+
}
|
|
30
|
+
function readBaselineInfo(cwd) {
|
|
31
|
+
let path = join(cwd, '.vizzly', 'baselines');
|
|
32
|
+
let metadata = readJsonFile(join(path, 'metadata.json'));
|
|
33
|
+
return {
|
|
34
|
+
count: metadata?.screenshots?.length || 0,
|
|
35
|
+
path: metadata ? path : null
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function readServerInfo(cwd) {
|
|
39
|
+
let serverInfo = readJsonFile(join(cwd, '.vizzly', 'server.json'));
|
|
40
|
+
return {
|
|
41
|
+
running: !!serverInfo,
|
|
42
|
+
port: serverInfo ? serverInfo.port : null
|
|
43
|
+
};
|
|
44
|
+
}
|
|
14
45
|
|
|
15
46
|
/**
|
|
16
47
|
* Get dynamic context about the current Vizzly state
|
|
@@ -22,46 +53,12 @@ export function getContext() {
|
|
|
22
53
|
let items = [];
|
|
23
54
|
try {
|
|
24
55
|
let cwd = process.cwd();
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
// Load global config once
|
|
28
|
-
let globalConfig = {};
|
|
29
|
-
try {
|
|
30
|
-
if (existsSync(globalConfigPath)) {
|
|
31
|
-
globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf8'));
|
|
32
|
-
}
|
|
33
|
-
} catch {
|
|
34
|
-
// Ignore
|
|
35
|
-
}
|
|
56
|
+
let globalConfig = readGlobalConfig();
|
|
36
57
|
|
|
37
58
|
// Check for vizzly.config.js (project config)
|
|
38
59
|
let hasProjectConfig = existsSync(join(cwd, 'vizzly.config.js'));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
let baselineCount = 0;
|
|
42
|
-
try {
|
|
43
|
-
let metaPath = join(cwd, '.vizzly', 'baselines', 'metadata.json');
|
|
44
|
-
if (existsSync(metaPath)) {
|
|
45
|
-
let meta = JSON.parse(readFileSync(metaPath, 'utf8'));
|
|
46
|
-
baselineCount = meta.screenshots?.length || 0;
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
// Ignore
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Check for TDD server running
|
|
53
|
-
let serverRunning = false;
|
|
54
|
-
let serverPort = null;
|
|
55
|
-
try {
|
|
56
|
-
let serverFile = join(cwd, '.vizzly', 'server.json');
|
|
57
|
-
if (existsSync(serverFile)) {
|
|
58
|
-
let serverInfo = JSON.parse(readFileSync(serverFile, 'utf8'));
|
|
59
|
-
serverPort = serverInfo.port;
|
|
60
|
-
serverRunning = true;
|
|
61
|
-
}
|
|
62
|
-
} catch {
|
|
63
|
-
// Ignore
|
|
64
|
-
}
|
|
60
|
+
let baselineInfo = readBaselineInfo(cwd);
|
|
61
|
+
let serverInfo = readServerInfo(cwd);
|
|
65
62
|
|
|
66
63
|
// Check for OAuth login (from vizzly login)
|
|
67
64
|
let isLoggedIn = !!globalConfig.auth?.accessToken;
|
|
@@ -71,11 +68,11 @@ export function getContext() {
|
|
|
71
68
|
let hasEnvToken = !!process.env.VIZZLY_TOKEN;
|
|
72
69
|
|
|
73
70
|
// Build context items - prioritize most useful info
|
|
74
|
-
if (
|
|
71
|
+
if (serverInfo.running) {
|
|
75
72
|
items.push({
|
|
76
73
|
type: 'success',
|
|
77
74
|
label: 'TDD Server',
|
|
78
|
-
value: `running on :${
|
|
75
|
+
value: `running on :${serverInfo.port}`
|
|
79
76
|
});
|
|
80
77
|
}
|
|
81
78
|
if (isLoggedIn && userName) {
|
|
@@ -97,14 +94,14 @@ export function getContext() {
|
|
|
97
94
|
value: 'run vizzly login'
|
|
98
95
|
});
|
|
99
96
|
}
|
|
100
|
-
if (
|
|
97
|
+
if (baselineInfo.count > 0) {
|
|
101
98
|
items.push({
|
|
102
99
|
type: 'success',
|
|
103
100
|
label: 'Baselines',
|
|
104
|
-
value: `${
|
|
101
|
+
value: `${baselineInfo.count} screenshots`
|
|
105
102
|
});
|
|
106
103
|
}
|
|
107
|
-
if (!hasProjectConfig && !
|
|
104
|
+
if (!hasProjectConfig && !serverInfo.running && baselineInfo.count === 0) {
|
|
108
105
|
// Only show "no config" hint if there's nothing else useful
|
|
109
106
|
items.push({
|
|
110
107
|
type: 'info',
|
|
@@ -126,7 +123,6 @@ export function getContext() {
|
|
|
126
123
|
*/
|
|
127
124
|
export function getDetailedContext() {
|
|
128
125
|
let cwd = process.cwd();
|
|
129
|
-
let globalConfigPath = join(process.env.VIZZLY_HOME || join(homedir(), '.vizzly'), 'config.json');
|
|
130
126
|
let context = {
|
|
131
127
|
tddServer: {
|
|
132
128
|
running: false,
|
|
@@ -146,42 +142,16 @@ export function getDetailedContext() {
|
|
|
146
142
|
}
|
|
147
143
|
};
|
|
148
144
|
try {
|
|
149
|
-
|
|
150
|
-
let globalConfig = {};
|
|
151
|
-
try {
|
|
152
|
-
if (existsSync(globalConfigPath)) {
|
|
153
|
-
globalConfig = JSON.parse(readFileSync(globalConfigPath, 'utf8'));
|
|
154
|
-
}
|
|
155
|
-
} catch {
|
|
156
|
-
// Ignore
|
|
157
|
-
}
|
|
145
|
+
let globalConfig = readGlobalConfig();
|
|
158
146
|
|
|
159
147
|
// Check for vizzly.config.js
|
|
160
148
|
context.project.hasConfig = existsSync(join(cwd, 'vizzly.config.js'));
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
context.baselines.count = meta.screenshots?.length || 0;
|
|
168
|
-
context.baselines.path = join(cwd, '.vizzly', 'baselines');
|
|
169
|
-
}
|
|
170
|
-
} catch {
|
|
171
|
-
// Ignore
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Check for TDD server
|
|
175
|
-
try {
|
|
176
|
-
let serverFile = join(cwd, '.vizzly', 'server.json');
|
|
177
|
-
if (existsSync(serverFile)) {
|
|
178
|
-
let serverInfo = JSON.parse(readFileSync(serverFile, 'utf8'));
|
|
179
|
-
context.tddServer.running = true;
|
|
180
|
-
context.tddServer.port = serverInfo.port;
|
|
181
|
-
}
|
|
182
|
-
} catch {
|
|
183
|
-
// Ignore
|
|
184
|
-
}
|
|
149
|
+
let baselineInfo = readBaselineInfo(cwd);
|
|
150
|
+
context.baselines.count = baselineInfo.count;
|
|
151
|
+
context.baselines.path = baselineInfo.path;
|
|
152
|
+
let serverInfo = readServerInfo(cwd);
|
|
153
|
+
context.tddServer.running = serverInfo.running;
|
|
154
|
+
context.tddServer.port = serverInfo.port;
|
|
185
155
|
|
|
186
156
|
// Check auth status
|
|
187
157
|
context.auth.loggedIn = !!globalConfig.auth?.accessToken;
|
|
@@ -85,6 +85,24 @@ export function getBuildName() {
|
|
|
85
85
|
return process.env.VIZZLY_BUILD_NAME;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Get comparison threshold from environment
|
|
90
|
+
* @returns {number|undefined} Threshold value when set
|
|
91
|
+
*/
|
|
92
|
+
export function getThreshold() {
|
|
93
|
+
if (process.env.VIZZLY_THRESHOLD === undefined) return undefined;
|
|
94
|
+
return Number(process.env.VIZZLY_THRESHOLD);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get minimum changed cluster size from environment
|
|
99
|
+
* @returns {number|undefined} Minimum cluster size when set
|
|
100
|
+
*/
|
|
101
|
+
export function getMinClusterSize() {
|
|
102
|
+
if (process.env.VIZZLY_MIN_CLUSTER_SIZE === undefined) return undefined;
|
|
103
|
+
return Number(process.env.VIZZLY_MIN_CLUSTER_SIZE);
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
/**
|
|
89
107
|
* Check if TDD mode is enabled
|
|
90
108
|
* @returns {boolean} Whether TDD mode is enabled
|
|
@@ -117,6 +135,8 @@ export function getAllEnvironmentConfig() {
|
|
|
117
135
|
buildId: getBuildId(),
|
|
118
136
|
buildName: getBuildName(),
|
|
119
137
|
parallelId: getParallelId(),
|
|
138
|
+
threshold: getThreshold(),
|
|
139
|
+
minClusterSize: getMinClusterSize(),
|
|
120
140
|
tddMode: isTddMode()
|
|
121
141
|
};
|
|
122
142
|
}
|
package/dist/utils/output.js
CHANGED
|
@@ -282,7 +282,7 @@ export function success(message, data = {}) {
|
|
|
282
282
|
});
|
|
283
283
|
if (config.silent) return;
|
|
284
284
|
if (config.json) {
|
|
285
|
-
console.
|
|
285
|
+
console.error(JSON.stringify({
|
|
286
286
|
status: 'success',
|
|
287
287
|
message,
|
|
288
288
|
...data
|
|
@@ -301,7 +301,7 @@ export function result(message) {
|
|
|
301
301
|
if (config.silent) return;
|
|
302
302
|
const elapsed = getElapsedTime();
|
|
303
303
|
if (config.json) {
|
|
304
|
-
console.
|
|
304
|
+
console.error(JSON.stringify({
|
|
305
305
|
status: 'complete',
|
|
306
306
|
message,
|
|
307
307
|
elapsed
|
|
@@ -319,7 +319,7 @@ export function info(message, data = {}) {
|
|
|
319
319
|
writeLog('info', message, data);
|
|
320
320
|
if (!shouldLog('info')) return;
|
|
321
321
|
if (config.json) {
|
|
322
|
-
console.
|
|
322
|
+
console.error(JSON.stringify({
|
|
323
323
|
status: 'info',
|
|
324
324
|
message,
|
|
325
325
|
...data
|
|
@@ -513,7 +513,7 @@ export function stopSpinner() {
|
|
|
513
513
|
export function progress(message, current = 0, total = 0) {
|
|
514
514
|
if (config.silent) return;
|
|
515
515
|
if (config.json) {
|
|
516
|
-
console.
|
|
516
|
+
console.error(JSON.stringify({
|
|
517
517
|
status: 'progress',
|
|
518
518
|
message,
|
|
519
519
|
progress: {
|
|
@@ -941,7 +941,7 @@ export function complete(message, options = {}) {
|
|
|
941
941
|
} = options;
|
|
942
942
|
let detailStr = detail ? ` ${colors.brand.textMuted(detail)}` : '';
|
|
943
943
|
if (config.json) {
|
|
944
|
-
console.
|
|
944
|
+
console.error(JSON.stringify({
|
|
945
945
|
status: 'complete',
|
|
946
946
|
message,
|
|
947
947
|
detail
|
|
@@ -1,21 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshot option names that are part of the SDK/config contract, not the
|
|
3
|
+
* user's arbitrary metadata bag.
|
|
4
|
+
*/
|
|
5
|
+
export let RESERVED_PROPERTY_OPTIONS = Object.freeze({
|
|
6
|
+
threshold: {
|
|
7
|
+
message: 'Move "threshold" out of properties; properties is only for user metadata.'
|
|
8
|
+
},
|
|
9
|
+
minClusterSize: {
|
|
10
|
+
message: 'Move "minClusterSize" out of properties; properties is only for user metadata.'
|
|
11
|
+
},
|
|
12
|
+
fullPage: {
|
|
13
|
+
message: 'Move "fullPage" out of properties; properties is only for user metadata.'
|
|
14
|
+
},
|
|
15
|
+
buildId: {
|
|
16
|
+
message: 'Move "buildId" out of properties; properties is only for user metadata.'
|
|
17
|
+
},
|
|
18
|
+
requestTimeout: {
|
|
19
|
+
message: 'Move "requestTimeout" out of properties; properties is only for user metadata.'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
function createReservedPropertyWarning(option) {
|
|
23
|
+
return {
|
|
24
|
+
code: 'reserved-property-option',
|
|
25
|
+
option,
|
|
26
|
+
message: RESERVED_PROPERTY_OPTIONS[option].message
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
1
30
|
/**
|
|
2
31
|
* Normalize screenshot SDK options into the properties payload consumed by the
|
|
3
32
|
* local TDD server and cloud-compatible comparison path.
|
|
4
33
|
*/
|
|
5
|
-
export function
|
|
34
|
+
export function normalizeScreenshotOptions(options = {}) {
|
|
6
35
|
let {
|
|
7
|
-
buildId
|
|
36
|
+
buildId,
|
|
8
37
|
properties = {},
|
|
9
|
-
requestTimeout
|
|
38
|
+
requestTimeout,
|
|
10
39
|
threshold,
|
|
11
40
|
minClusterSize,
|
|
12
|
-
fullPage
|
|
13
|
-
...topLevelProperties
|
|
41
|
+
fullPage
|
|
14
42
|
} = options;
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
43
|
+
let warnings = [];
|
|
44
|
+
let normalizedProperties = {};
|
|
45
|
+
for (let [key, value] of Object.entries(properties || {})) {
|
|
46
|
+
if (RESERVED_PROPERTY_OPTIONS[key]) {
|
|
47
|
+
warnings.push(createReservedPropertyWarning(key));
|
|
48
|
+
if (key === 'threshold' && threshold === undefined) threshold = value;
|
|
49
|
+
if (key === 'minClusterSize' && minClusterSize === undefined) {
|
|
50
|
+
minClusterSize = value;
|
|
51
|
+
}
|
|
52
|
+
if (key === 'fullPage' && fullPage === undefined) fullPage = value;
|
|
53
|
+
if (key === 'buildId' && buildId === undefined) buildId = value;
|
|
54
|
+
if (key === 'requestTimeout' && requestTimeout === undefined) {
|
|
55
|
+
requestTimeout = value;
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
normalizedProperties[key] = value;
|
|
60
|
+
}
|
|
19
61
|
if (threshold !== undefined) {
|
|
20
62
|
normalizedProperties.threshold = threshold;
|
|
21
63
|
}
|
|
@@ -25,5 +67,13 @@ export function createScreenshotProperties(options = {}) {
|
|
|
25
67
|
if (fullPage !== undefined) {
|
|
26
68
|
normalizedProperties.fullPage = fullPage;
|
|
27
69
|
}
|
|
28
|
-
return
|
|
70
|
+
return {
|
|
71
|
+
buildId,
|
|
72
|
+
requestTimeout,
|
|
73
|
+
properties: normalizedProperties,
|
|
74
|
+
warnings
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function createScreenshotProperties(options = {}) {
|
|
78
|
+
return normalizeScreenshotOptions(options).properties;
|
|
29
79
|
}
|