@vizzly-testing/cli 0.9.1 → 0.10.1
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 +77 -20
- package/dist/cli.js +46 -1
- package/dist/client/index.js +0 -2
- package/dist/commands/init.js +121 -14
- package/dist/plugin-loader.js +204 -0
- package/dist/reporter/reporter-bundle.css +1 -1
- package/dist/reporter/reporter-bundle.iife.js +13 -13
- package/dist/types/client/index.d.ts +0 -2
- package/dist/types/commands/init.d.ts +20 -1
- package/dist/types/plugin-loader.d.ts +8 -0
- package/dist/types/utils/config-loader.d.ts +1 -1
- package/dist/types/utils/config-schema.d.ts +217 -0
- package/dist/utils/config-loader.js +23 -12
- package/dist/utils/config-schema.js +134 -0
- package/docs/api-reference.md +1 -2
- package/docs/plugins.md +496 -0
- package/package.json +4 -3
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
* @param {Object} [options] - Optional configuration
|
|
7
7
|
* @param {Record<string, any>} [options.properties] - Additional properties to attach to the screenshot
|
|
8
8
|
* @param {number} [options.threshold=0] - Pixel difference threshold (0-100)
|
|
9
|
-
* @param {string} [options.variant] - Variant name for organizing screenshots
|
|
10
9
|
* @param {boolean} [options.fullPage=false] - Whether this is a full page screenshot
|
|
11
10
|
*
|
|
12
11
|
* @returns {Promise<void>}
|
|
@@ -33,7 +32,6 @@
|
|
|
33
32
|
export function vizzlyScreenshot(name: string, imageBuffer: Buffer, options?: {
|
|
34
33
|
properties?: Record<string, any>;
|
|
35
34
|
threshold?: number;
|
|
36
|
-
variant?: string;
|
|
37
35
|
fullPage?: boolean;
|
|
38
36
|
}): Promise<void>;
|
|
39
37
|
/**
|
|
@@ -5,10 +5,29 @@ export function init(options?: {}): Promise<void>;
|
|
|
5
5
|
* Simple configuration setup for Vizzly CLI
|
|
6
6
|
*/
|
|
7
7
|
export class InitCommand {
|
|
8
|
-
constructor(logger: any);
|
|
8
|
+
constructor(logger: any, plugins?: any[]);
|
|
9
9
|
logger: any;
|
|
10
|
+
plugins: any[];
|
|
10
11
|
run(options?: {}): Promise<void>;
|
|
11
12
|
generateConfigFile(configPath: any): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Generate configuration sections for plugins
|
|
15
|
+
* @returns {string} Plugin config sections as formatted string
|
|
16
|
+
*/
|
|
17
|
+
generatePluginConfigs(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Format a plugin's config schema as JavaScript code
|
|
20
|
+
* @param {Object} plugin - Plugin with configSchema
|
|
21
|
+
* @returns {string} Formatted config string
|
|
22
|
+
*/
|
|
23
|
+
formatPluginConfig(plugin: any): string;
|
|
24
|
+
/**
|
|
25
|
+
* Format a JavaScript value with proper indentation
|
|
26
|
+
* @param {*} value - Value to format
|
|
27
|
+
* @param {number} depth - Current indentation depth
|
|
28
|
+
* @returns {string} Formatted value
|
|
29
|
+
*/
|
|
30
|
+
formatValue(value: any, depth?: number): string;
|
|
12
31
|
showNextSteps(): void;
|
|
13
32
|
fileExists(filePath: any): Promise<boolean>;
|
|
14
33
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load and register plugins from node_modules and config
|
|
3
|
+
* @param {string|null} configPath - Path to config file
|
|
4
|
+
* @param {Object} config - Loaded configuration
|
|
5
|
+
* @param {Object} logger - Logger instance
|
|
6
|
+
* @returns {Promise<Array>} Array of loaded plugins
|
|
7
|
+
*/
|
|
8
|
+
export function loadPlugins(configPath: string | null, config: any, logger: any): Promise<any[]>;
|
|
@@ -2,7 +2,6 @@ export function loadConfig(configPath?: any, cliOverrides?: {}): Promise<{
|
|
|
2
2
|
server: {
|
|
3
3
|
port: number;
|
|
4
4
|
timeout: number;
|
|
5
|
-
screenshotPath: string;
|
|
6
5
|
};
|
|
7
6
|
build: {
|
|
8
7
|
name: string;
|
|
@@ -19,6 +18,7 @@ export function loadConfig(configPath?: any, cliOverrides?: {}): Promise<{
|
|
|
19
18
|
tdd: {
|
|
20
19
|
openReport: boolean;
|
|
21
20
|
};
|
|
21
|
+
plugins: any[];
|
|
22
22
|
apiKey: string;
|
|
23
23
|
apiUrl: string;
|
|
24
24
|
}>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Vizzly configuration
|
|
3
|
+
* @param {unknown} config - Configuration to validate
|
|
4
|
+
* @returns {Object} Validated configuration
|
|
5
|
+
* @throws {ZodError} If validation fails
|
|
6
|
+
*/
|
|
7
|
+
export function validateVizzlyConfig(config: unknown): any;
|
|
8
|
+
/**
|
|
9
|
+
* Safely validate with defaults if config is missing
|
|
10
|
+
* @param {unknown} config - Configuration to validate (can be undefined)
|
|
11
|
+
* @returns {Object} Validated configuration with defaults
|
|
12
|
+
*/
|
|
13
|
+
export function validateVizzlyConfigWithDefaults(config: unknown): any;
|
|
14
|
+
/**
|
|
15
|
+
* Core Vizzly configuration schema
|
|
16
|
+
* Allows plugin-specific keys with passthrough for extensibility
|
|
17
|
+
*/
|
|
18
|
+
export let vizzlyConfigSchema: z.ZodDefault<z.ZodObject<{
|
|
19
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
20
|
+
apiUrl: z.ZodOptional<z.ZodString>;
|
|
21
|
+
server: z.ZodDefault<z.ZodObject<{
|
|
22
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
23
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
port?: number;
|
|
26
|
+
timeout?: number;
|
|
27
|
+
}, {
|
|
28
|
+
port?: number;
|
|
29
|
+
timeout?: number;
|
|
30
|
+
}>>;
|
|
31
|
+
build: z.ZodDefault<z.ZodObject<{
|
|
32
|
+
name: z.ZodDefault<z.ZodString>;
|
|
33
|
+
environment: z.ZodDefault<z.ZodString>;
|
|
34
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
35
|
+
commit: z.ZodOptional<z.ZodString>;
|
|
36
|
+
message: z.ZodOptional<z.ZodString>;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
name?: string;
|
|
39
|
+
message?: string;
|
|
40
|
+
environment?: string;
|
|
41
|
+
branch?: string;
|
|
42
|
+
commit?: string;
|
|
43
|
+
}, {
|
|
44
|
+
name?: string;
|
|
45
|
+
message?: string;
|
|
46
|
+
environment?: string;
|
|
47
|
+
branch?: string;
|
|
48
|
+
commit?: string;
|
|
49
|
+
}>>;
|
|
50
|
+
upload: z.ZodDefault<z.ZodObject<{
|
|
51
|
+
screenshotsDir: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
52
|
+
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
53
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
54
|
+
}, "strip", z.ZodTypeAny, {
|
|
55
|
+
timeout?: number;
|
|
56
|
+
screenshotsDir?: string | string[];
|
|
57
|
+
batchSize?: number;
|
|
58
|
+
}, {
|
|
59
|
+
timeout?: number;
|
|
60
|
+
screenshotsDir?: string | string[];
|
|
61
|
+
batchSize?: number;
|
|
62
|
+
}>>;
|
|
63
|
+
comparison: z.ZodDefault<z.ZodObject<{
|
|
64
|
+
threshold: z.ZodDefault<z.ZodNumber>;
|
|
65
|
+
}, "strip", z.ZodTypeAny, {
|
|
66
|
+
threshold?: number;
|
|
67
|
+
}, {
|
|
68
|
+
threshold?: number;
|
|
69
|
+
}>>;
|
|
70
|
+
tdd: z.ZodDefault<z.ZodObject<{
|
|
71
|
+
openReport: z.ZodDefault<z.ZodBoolean>;
|
|
72
|
+
}, "strip", z.ZodTypeAny, {
|
|
73
|
+
openReport?: boolean;
|
|
74
|
+
}, {
|
|
75
|
+
openReport?: boolean;
|
|
76
|
+
}>>;
|
|
77
|
+
plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
78
|
+
parallelId: z.ZodOptional<z.ZodString>;
|
|
79
|
+
baselineBuildId: z.ZodOptional<z.ZodString>;
|
|
80
|
+
baselineComparisonId: z.ZodOptional<z.ZodString>;
|
|
81
|
+
eager: z.ZodOptional<z.ZodBoolean>;
|
|
82
|
+
wait: z.ZodOptional<z.ZodBoolean>;
|
|
83
|
+
allowNoToken: z.ZodOptional<z.ZodBoolean>;
|
|
84
|
+
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
85
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
86
|
+
apiUrl: z.ZodOptional<z.ZodString>;
|
|
87
|
+
server: z.ZodDefault<z.ZodObject<{
|
|
88
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
89
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
90
|
+
}, "strip", z.ZodTypeAny, {
|
|
91
|
+
port?: number;
|
|
92
|
+
timeout?: number;
|
|
93
|
+
}, {
|
|
94
|
+
port?: number;
|
|
95
|
+
timeout?: number;
|
|
96
|
+
}>>;
|
|
97
|
+
build: z.ZodDefault<z.ZodObject<{
|
|
98
|
+
name: z.ZodDefault<z.ZodString>;
|
|
99
|
+
environment: z.ZodDefault<z.ZodString>;
|
|
100
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
101
|
+
commit: z.ZodOptional<z.ZodString>;
|
|
102
|
+
message: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, "strip", z.ZodTypeAny, {
|
|
104
|
+
name?: string;
|
|
105
|
+
message?: string;
|
|
106
|
+
environment?: string;
|
|
107
|
+
branch?: string;
|
|
108
|
+
commit?: string;
|
|
109
|
+
}, {
|
|
110
|
+
name?: string;
|
|
111
|
+
message?: string;
|
|
112
|
+
environment?: string;
|
|
113
|
+
branch?: string;
|
|
114
|
+
commit?: string;
|
|
115
|
+
}>>;
|
|
116
|
+
upload: z.ZodDefault<z.ZodObject<{
|
|
117
|
+
screenshotsDir: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
118
|
+
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
119
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
120
|
+
}, "strip", z.ZodTypeAny, {
|
|
121
|
+
timeout?: number;
|
|
122
|
+
screenshotsDir?: string | string[];
|
|
123
|
+
batchSize?: number;
|
|
124
|
+
}, {
|
|
125
|
+
timeout?: number;
|
|
126
|
+
screenshotsDir?: string | string[];
|
|
127
|
+
batchSize?: number;
|
|
128
|
+
}>>;
|
|
129
|
+
comparison: z.ZodDefault<z.ZodObject<{
|
|
130
|
+
threshold: z.ZodDefault<z.ZodNumber>;
|
|
131
|
+
}, "strip", z.ZodTypeAny, {
|
|
132
|
+
threshold?: number;
|
|
133
|
+
}, {
|
|
134
|
+
threshold?: number;
|
|
135
|
+
}>>;
|
|
136
|
+
tdd: z.ZodDefault<z.ZodObject<{
|
|
137
|
+
openReport: z.ZodDefault<z.ZodBoolean>;
|
|
138
|
+
}, "strip", z.ZodTypeAny, {
|
|
139
|
+
openReport?: boolean;
|
|
140
|
+
}, {
|
|
141
|
+
openReport?: boolean;
|
|
142
|
+
}>>;
|
|
143
|
+
plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
144
|
+
parallelId: z.ZodOptional<z.ZodString>;
|
|
145
|
+
baselineBuildId: z.ZodOptional<z.ZodString>;
|
|
146
|
+
baselineComparisonId: z.ZodOptional<z.ZodString>;
|
|
147
|
+
eager: z.ZodOptional<z.ZodBoolean>;
|
|
148
|
+
wait: z.ZodOptional<z.ZodBoolean>;
|
|
149
|
+
allowNoToken: z.ZodOptional<z.ZodBoolean>;
|
|
150
|
+
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
151
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
152
|
+
apiUrl: z.ZodOptional<z.ZodString>;
|
|
153
|
+
server: z.ZodDefault<z.ZodObject<{
|
|
154
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
155
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
156
|
+
}, "strip", z.ZodTypeAny, {
|
|
157
|
+
port?: number;
|
|
158
|
+
timeout?: number;
|
|
159
|
+
}, {
|
|
160
|
+
port?: number;
|
|
161
|
+
timeout?: number;
|
|
162
|
+
}>>;
|
|
163
|
+
build: z.ZodDefault<z.ZodObject<{
|
|
164
|
+
name: z.ZodDefault<z.ZodString>;
|
|
165
|
+
environment: z.ZodDefault<z.ZodString>;
|
|
166
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
167
|
+
commit: z.ZodOptional<z.ZodString>;
|
|
168
|
+
message: z.ZodOptional<z.ZodString>;
|
|
169
|
+
}, "strip", z.ZodTypeAny, {
|
|
170
|
+
name?: string;
|
|
171
|
+
message?: string;
|
|
172
|
+
environment?: string;
|
|
173
|
+
branch?: string;
|
|
174
|
+
commit?: string;
|
|
175
|
+
}, {
|
|
176
|
+
name?: string;
|
|
177
|
+
message?: string;
|
|
178
|
+
environment?: string;
|
|
179
|
+
branch?: string;
|
|
180
|
+
commit?: string;
|
|
181
|
+
}>>;
|
|
182
|
+
upload: z.ZodDefault<z.ZodObject<{
|
|
183
|
+
screenshotsDir: z.ZodDefault<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
184
|
+
batchSize: z.ZodDefault<z.ZodNumber>;
|
|
185
|
+
timeout: z.ZodDefault<z.ZodNumber>;
|
|
186
|
+
}, "strip", z.ZodTypeAny, {
|
|
187
|
+
timeout?: number;
|
|
188
|
+
screenshotsDir?: string | string[];
|
|
189
|
+
batchSize?: number;
|
|
190
|
+
}, {
|
|
191
|
+
timeout?: number;
|
|
192
|
+
screenshotsDir?: string | string[];
|
|
193
|
+
batchSize?: number;
|
|
194
|
+
}>>;
|
|
195
|
+
comparison: z.ZodDefault<z.ZodObject<{
|
|
196
|
+
threshold: z.ZodDefault<z.ZodNumber>;
|
|
197
|
+
}, "strip", z.ZodTypeAny, {
|
|
198
|
+
threshold?: number;
|
|
199
|
+
}, {
|
|
200
|
+
threshold?: number;
|
|
201
|
+
}>>;
|
|
202
|
+
tdd: z.ZodDefault<z.ZodObject<{
|
|
203
|
+
openReport: z.ZodDefault<z.ZodBoolean>;
|
|
204
|
+
}, "strip", z.ZodTypeAny, {
|
|
205
|
+
openReport?: boolean;
|
|
206
|
+
}, {
|
|
207
|
+
openReport?: boolean;
|
|
208
|
+
}>>;
|
|
209
|
+
plugins: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
210
|
+
parallelId: z.ZodOptional<z.ZodString>;
|
|
211
|
+
baselineBuildId: z.ZodOptional<z.ZodString>;
|
|
212
|
+
baselineComparisonId: z.ZodOptional<z.ZodString>;
|
|
213
|
+
eager: z.ZodOptional<z.ZodBoolean>;
|
|
214
|
+
wait: z.ZodOptional<z.ZodBoolean>;
|
|
215
|
+
allowNoToken: z.ZodOptional<z.ZodBoolean>;
|
|
216
|
+
}, z.ZodTypeAny, "passthrough">>>;
|
|
217
|
+
import { z } from 'zod';
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
3
|
import { getApiToken, getApiUrl, getParallelId } from './environment-config.js';
|
|
4
|
+
import { validateVizzlyConfigWithDefaults } from './config-schema.js';
|
|
4
5
|
const DEFAULT_CONFIG = {
|
|
5
6
|
// API Configuration
|
|
6
7
|
apiKey: getApiToken(),
|
|
@@ -8,8 +9,7 @@ const DEFAULT_CONFIG = {
|
|
|
8
9
|
// Server Configuration (for run command)
|
|
9
10
|
server: {
|
|
10
11
|
port: 47392,
|
|
11
|
-
timeout: 30000
|
|
12
|
-
screenshotPath: '/screenshot'
|
|
12
|
+
timeout: 30000
|
|
13
13
|
},
|
|
14
14
|
// Build Configuration
|
|
15
15
|
build: {
|
|
@@ -29,9 +29,23 @@ const DEFAULT_CONFIG = {
|
|
|
29
29
|
// TDD Configuration
|
|
30
30
|
tdd: {
|
|
31
31
|
openReport: false // Whether to auto-open HTML report in browser
|
|
32
|
-
}
|
|
32
|
+
},
|
|
33
|
+
// Plugins
|
|
34
|
+
plugins: []
|
|
33
35
|
};
|
|
34
36
|
export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
37
|
+
// 1. Load from config file using cosmiconfig
|
|
38
|
+
const explorer = cosmiconfigSync('vizzly');
|
|
39
|
+
const result = configPath ? explorer.load(configPath) : explorer.search();
|
|
40
|
+
let fileConfig = {};
|
|
41
|
+
if (result && result.config) {
|
|
42
|
+
// Handle ESM default export (cosmiconfig wraps it in { default: {...} })
|
|
43
|
+
fileConfig = result.config.default || result.config;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 2. Validate config file using Zod schema
|
|
47
|
+
const validatedFileConfig = validateVizzlyConfigWithDefaults(fileConfig);
|
|
48
|
+
|
|
35
49
|
// Create a proper clone of the default config to avoid shared object references
|
|
36
50
|
const config = {
|
|
37
51
|
...DEFAULT_CONFIG,
|
|
@@ -49,17 +63,14 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
49
63
|
},
|
|
50
64
|
tdd: {
|
|
51
65
|
...DEFAULT_CONFIG.tdd
|
|
52
|
-
}
|
|
66
|
+
},
|
|
67
|
+
plugins: [...DEFAULT_CONFIG.plugins]
|
|
53
68
|
};
|
|
54
69
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
const result = configPath ? explorer.load(configPath) : explorer.search();
|
|
58
|
-
if (result && result.config) {
|
|
59
|
-
mergeConfig(config, result.config);
|
|
60
|
-
}
|
|
70
|
+
// Merge validated file config
|
|
71
|
+
mergeConfig(config, validatedFileConfig);
|
|
61
72
|
|
|
62
|
-
//
|
|
73
|
+
// 3. Override with environment variables
|
|
63
74
|
const envApiKey = getApiToken();
|
|
64
75
|
const envApiUrl = getApiUrl();
|
|
65
76
|
const envParallelId = getParallelId();
|
|
@@ -67,7 +78,7 @@ export async function loadConfig(configPath = null, cliOverrides = {}) {
|
|
|
67
78
|
if (envApiUrl !== 'https://app.vizzly.dev') config.apiUrl = envApiUrl;
|
|
68
79
|
if (envParallelId) config.parallelId = envParallelId;
|
|
69
80
|
|
|
70
|
-
//
|
|
81
|
+
// 4. Apply CLI overrides (highest priority)
|
|
71
82
|
applyCLIOverrides(config, cliOverrides);
|
|
72
83
|
return config;
|
|
73
84
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration schema validation for Vizzly CLI
|
|
3
|
+
* Uses Zod for runtime validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Server configuration schema
|
|
10
|
+
*/
|
|
11
|
+
let serverSchema = z.object({
|
|
12
|
+
port: z.number().int().positive().default(47392),
|
|
13
|
+
timeout: z.number().int().positive().default(30000)
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Build configuration schema
|
|
18
|
+
*/
|
|
19
|
+
let buildSchema = z.object({
|
|
20
|
+
name: z.string().default('Build {timestamp}'),
|
|
21
|
+
environment: z.string().default('test'),
|
|
22
|
+
branch: z.string().optional(),
|
|
23
|
+
commit: z.string().optional(),
|
|
24
|
+
message: z.string().optional()
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Upload configuration schema
|
|
29
|
+
*/
|
|
30
|
+
let uploadSchema = z.object({
|
|
31
|
+
screenshotsDir: z.union([z.string(), z.array(z.string())]).default('./screenshots'),
|
|
32
|
+
batchSize: z.number().int().positive().default(10),
|
|
33
|
+
timeout: z.number().int().positive().default(30000)
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Comparison configuration schema
|
|
38
|
+
*/
|
|
39
|
+
let comparisonSchema = z.object({
|
|
40
|
+
threshold: z.number().min(0).max(1).default(0.1)
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* TDD configuration schema
|
|
45
|
+
*/
|
|
46
|
+
let tddSchema = z.object({
|
|
47
|
+
openReport: z.boolean().default(false)
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Core Vizzly configuration schema
|
|
52
|
+
* Allows plugin-specific keys with passthrough for extensibility
|
|
53
|
+
*/
|
|
54
|
+
export let vizzlyConfigSchema = z.object({
|
|
55
|
+
// Core Vizzly config
|
|
56
|
+
apiKey: z.string().optional(),
|
|
57
|
+
apiUrl: z.string().url().optional(),
|
|
58
|
+
server: serverSchema.default({
|
|
59
|
+
port: 47392,
|
|
60
|
+
timeout: 30000
|
|
61
|
+
}),
|
|
62
|
+
build: buildSchema.default({
|
|
63
|
+
name: 'Build {timestamp}',
|
|
64
|
+
environment: 'test'
|
|
65
|
+
}),
|
|
66
|
+
upload: uploadSchema.default({
|
|
67
|
+
screenshotsDir: './screenshots',
|
|
68
|
+
batchSize: 10,
|
|
69
|
+
timeout: 30000
|
|
70
|
+
}),
|
|
71
|
+
comparison: comparisonSchema.default({
|
|
72
|
+
threshold: 0.1
|
|
73
|
+
}),
|
|
74
|
+
tdd: tddSchema.default({
|
|
75
|
+
openReport: false
|
|
76
|
+
}),
|
|
77
|
+
plugins: z.array(z.string()).default([]),
|
|
78
|
+
// Additional optional fields
|
|
79
|
+
parallelId: z.string().optional(),
|
|
80
|
+
baselineBuildId: z.string().optional(),
|
|
81
|
+
baselineComparisonId: z.string().optional(),
|
|
82
|
+
eager: z.boolean().optional(),
|
|
83
|
+
wait: z.boolean().optional(),
|
|
84
|
+
allowNoToken: z.boolean().optional()
|
|
85
|
+
}).passthrough() // Allow plugin-specific keys like `staticSite`, `storybook`, etc.
|
|
86
|
+
.default({
|
|
87
|
+
server: {
|
|
88
|
+
port: 47392,
|
|
89
|
+
timeout: 30000
|
|
90
|
+
},
|
|
91
|
+
build: {
|
|
92
|
+
name: 'Build {timestamp}',
|
|
93
|
+
environment: 'test'
|
|
94
|
+
},
|
|
95
|
+
upload: {
|
|
96
|
+
screenshotsDir: './screenshots',
|
|
97
|
+
batchSize: 10,
|
|
98
|
+
timeout: 30000
|
|
99
|
+
},
|
|
100
|
+
comparison: {
|
|
101
|
+
threshold: 0.1
|
|
102
|
+
},
|
|
103
|
+
tdd: {
|
|
104
|
+
openReport: false
|
|
105
|
+
},
|
|
106
|
+
plugins: []
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Validate Vizzly configuration
|
|
111
|
+
* @param {unknown} config - Configuration to validate
|
|
112
|
+
* @returns {Object} Validated configuration
|
|
113
|
+
* @throws {ZodError} If validation fails
|
|
114
|
+
*/
|
|
115
|
+
export function validateVizzlyConfig(config) {
|
|
116
|
+
try {
|
|
117
|
+
return vizzlyConfigSchema.parse(config);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Re-throw with more context
|
|
120
|
+
throw new Error(`Invalid Vizzly configuration: ${error.message}\n\n` + `Please check your vizzly.config.js file.\n` + `See https://vizzly.dev/docs/configuration for configuration options.`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Safely validate with defaults if config is missing
|
|
126
|
+
* @param {unknown} config - Configuration to validate (can be undefined)
|
|
127
|
+
* @returns {Object} Validated configuration with defaults
|
|
128
|
+
*/
|
|
129
|
+
export function validateVizzlyConfigWithDefaults(config) {
|
|
130
|
+
if (!config) {
|
|
131
|
+
return vizzlyConfigSchema.parse({});
|
|
132
|
+
}
|
|
133
|
+
return validateVizzlyConfig(config);
|
|
134
|
+
}
|
package/docs/api-reference.md
CHANGED
|
@@ -476,8 +476,7 @@ Configuration loaded via cosmiconfig in this order:
|
|
|
476
476
|
// Server Configuration (for run command)
|
|
477
477
|
server: {
|
|
478
478
|
port: number, // Server port (default: 47392)
|
|
479
|
-
timeout: number
|
|
480
|
-
screenshotPath: string // Screenshot endpoint path
|
|
479
|
+
timeout: number // Timeout in ms (default: 30000)
|
|
481
480
|
},
|
|
482
481
|
|
|
483
482
|
// Build Configuration
|