@vizzly-testing/cli 0.36.0 → 0.38.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 +23 -17
- package/dist/api/client.js +14 -2
- package/dist/api/core.js +6 -0
- package/dist/cli.js +54 -15
- package/dist/commands/api.js +72 -25
- package/dist/commands/init.js +4 -2
- package/dist/commands/run.js +10 -6
- package/dist/commands/status.js +2 -2
- package/dist/commands/tdd-screenshots.js +417 -0
- package/dist/commands/tdd.js +6 -2
- package/dist/commands/upload.js +5 -1
- package/dist/reporter/reporter-bundle.iife.js +38 -38
- package/dist/test-runner/core.js +2 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/uploader/core.js +2 -0
- package/dist/utils/ci-env.js +43 -4
- package/dist/utils/git.js +46 -6
- package/package.json +8 -7
- package/skills/vizzly/SKILL.md +17 -9
- package/skills/vizzly/references/cli-context.md +74 -15
- package/skills/vizzly/references/setup-ci.md +2 -2
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { basename, join, resolve } from 'node:path';
|
|
3
|
+
import { generateScreenshotSignature } from '../tdd/core/signature.js';
|
|
4
|
+
import { TddService as defaultTddService } from '../tdd/tdd-service.js';
|
|
5
|
+
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
6
|
+
import * as output from '../utils/output.js';
|
|
7
|
+
import { safePath } from '../utils/security.js';
|
|
8
|
+
let DEFAULT_PAGE_SIZE = 20;
|
|
9
|
+
let MAX_PAGE_SIZE = 100;
|
|
10
|
+
let TDD_SCREENSHOT_STATUSES = new Set(['baseline-created', 'baseline-updated', 'error', 'failed', 'new', 'passed', 'rejected']);
|
|
11
|
+
let TDD_SCREENSHOT_IMAGES = new Set(['current', 'baseline', 'diff', 'all']);
|
|
12
|
+
function readLocalReport(workingDir) {
|
|
13
|
+
let projectDir = resolve(workingDir);
|
|
14
|
+
let vizzlyDir = join(projectDir, '.vizzly');
|
|
15
|
+
let reportPath = join(vizzlyDir, 'report-data.json');
|
|
16
|
+
if (!existsSync(reportPath)) {
|
|
17
|
+
return {
|
|
18
|
+
error: `No local TDD screenshots found. Run "vizzly tdd run <command>" ` + `first. Expected ${reportPath}`
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
let report = JSON.parse(readFileSync(reportPath, 'utf8'));
|
|
23
|
+
if (!report || !Array.isArray(report.comparisons)) {
|
|
24
|
+
return {
|
|
25
|
+
error: `Local TDD report has no comparisons: ${reportPath}`
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (report.comparisons.some(comparison => !comparison || typeof comparison !== 'object' || Array.isArray(comparison))) {
|
|
29
|
+
return {
|
|
30
|
+
error: `Local TDD report has invalid comparisons: ${reportPath}`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
report,
|
|
35
|
+
projectDir,
|
|
36
|
+
vizzlyDir
|
|
37
|
+
};
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return {
|
|
40
|
+
error: `Could not read local TDD report ${reportPath}: ${error.message}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function resolveImagePath(imageUrl, vizzlyDir) {
|
|
45
|
+
if (typeof imageUrl !== 'string' || imageUrl.length === 0) return null;
|
|
46
|
+
let relativePath;
|
|
47
|
+
if (imageUrl.startsWith('/images/')) {
|
|
48
|
+
relativePath = imageUrl.slice('/images/'.length);
|
|
49
|
+
} else if (imageUrl.startsWith('.vizzly/')) {
|
|
50
|
+
relativePath = imageUrl.slice('.vizzly/'.length);
|
|
51
|
+
} else {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return safePath(vizzlyDir, relativePath);
|
|
56
|
+
} catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function readComparisonDetails(vizzlyDir) {
|
|
61
|
+
let detailsPath = join(vizzlyDir, 'comparison-details.json');
|
|
62
|
+
if (!existsSync(detailsPath)) return {};
|
|
63
|
+
try {
|
|
64
|
+
let details = JSON.parse(readFileSync(detailsPath, 'utf8'));
|
|
65
|
+
return details && typeof details === 'object' && !Array.isArray(details) ? details : {};
|
|
66
|
+
} catch {
|
|
67
|
+
return {};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function getViewport(properties) {
|
|
71
|
+
return {
|
|
72
|
+
width: properties?.viewport_width ?? properties?.viewport?.width ?? null,
|
|
73
|
+
height: properties?.viewport_height ?? properties?.viewport?.height ?? null
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function comparisonSummary(comparison, vizzlyDir) {
|
|
77
|
+
let currentPath = resolveImagePath(comparison.current, vizzlyDir);
|
|
78
|
+
return {
|
|
79
|
+
id: comparison.id,
|
|
80
|
+
name: comparison.name,
|
|
81
|
+
status: comparison.status,
|
|
82
|
+
diffPercentage: comparison.diffPercentage ?? null,
|
|
83
|
+
browser: comparison.properties?.browser ?? null,
|
|
84
|
+
viewport: getViewport(comparison.properties),
|
|
85
|
+
currentImage: currentPath ? {
|
|
86
|
+
path: currentPath,
|
|
87
|
+
exists: existsSync(currentPath)
|
|
88
|
+
} : null
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function getPaginationOptions(options = {}) {
|
|
92
|
+
let page = options.page ?? 1;
|
|
93
|
+
let pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
94
|
+
let errors = [];
|
|
95
|
+
if (!Number.isInteger(page) || page < 1) {
|
|
96
|
+
errors.push('--page must be a positive integer');
|
|
97
|
+
}
|
|
98
|
+
if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > MAX_PAGE_SIZE) {
|
|
99
|
+
errors.push(`--page-size must be an integer between 1 and ${MAX_PAGE_SIZE}`);
|
|
100
|
+
}
|
|
101
|
+
if (options.status && !TDD_SCREENSHOT_STATUSES.has(options.status)) {
|
|
102
|
+
errors.push(`--status must be one of: ${[...TDD_SCREENSHOT_STATUSES].join(', ')}`);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
page,
|
|
106
|
+
pageSize,
|
|
107
|
+
errors
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function reportError(message, commandOutput = output) {
|
|
111
|
+
commandOutput.error(message);
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
}
|
|
114
|
+
function getImageKinds(image = 'current') {
|
|
115
|
+
if (!TDD_SCREENSHOT_IMAGES.has(image)) {
|
|
116
|
+
return {
|
|
117
|
+
error: `--image must be one of: ${[...TDD_SCREENSHOT_IMAGES].join(', ')}`
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
kinds: image === 'all' ? ['current', 'baseline', 'diff'] : [image]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function imageReferences(comparison, vizzlyDir, kinds) {
|
|
125
|
+
return Object.fromEntries(kinds.map(kind => {
|
|
126
|
+
let path = resolveImagePath(comparison[kind], vizzlyDir);
|
|
127
|
+
return [kind, path ? {
|
|
128
|
+
path,
|
|
129
|
+
exists: existsSync(path)
|
|
130
|
+
} : null];
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
function readLocalComparison(idOrName, workingDir) {
|
|
134
|
+
if (!idOrName?.trim()) {
|
|
135
|
+
return {
|
|
136
|
+
error: 'A screenshot name or comparison ID is required.'
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
let local = readLocalReport(workingDir);
|
|
140
|
+
if (local.error) return local;
|
|
141
|
+
let found = findComparison(local.report.comparisons, idOrName);
|
|
142
|
+
if (found.error) return found;
|
|
143
|
+
return {
|
|
144
|
+
local,
|
|
145
|
+
comparison: found.comparison
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function listTddScreenshots(options = {}, workingDir = process.cwd()) {
|
|
149
|
+
let {
|
|
150
|
+
page,
|
|
151
|
+
pageSize,
|
|
152
|
+
errors
|
|
153
|
+
} = getPaginationOptions(options);
|
|
154
|
+
if (errors.length > 0) {
|
|
155
|
+
reportError(errors.join('\n'));
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
let local = readLocalReport(workingDir);
|
|
159
|
+
if (local.error) {
|
|
160
|
+
reportError(local.error);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
let comparisons = options.status ? local.report.comparisons.filter(item => item.status === options.status) : local.report.comparisons;
|
|
164
|
+
let total = comparisons.length;
|
|
165
|
+
let totalPages = Math.ceil(total / pageSize);
|
|
166
|
+
let start = (page - 1) * pageSize;
|
|
167
|
+
let items = comparisons.slice(start, start + pageSize).map(comparison => comparisonSummary(comparison, local.vizzlyDir));
|
|
168
|
+
let data = {
|
|
169
|
+
page,
|
|
170
|
+
pageSize,
|
|
171
|
+
total,
|
|
172
|
+
totalPages,
|
|
173
|
+
hasPrevious: page > 1 && total > 0,
|
|
174
|
+
hasNext: start + items.length < total,
|
|
175
|
+
screenshots: items
|
|
176
|
+
};
|
|
177
|
+
if (output.isJson()) {
|
|
178
|
+
output.data(data);
|
|
179
|
+
return data;
|
|
180
|
+
}
|
|
181
|
+
let pageLabel = totalPages ? ` of ${totalPages}` : '';
|
|
182
|
+
output.print(`TDD screenshots · ${total} comparison${total === 1 ? '' : 's'} · ` + `page ${page}${pageLabel}`);
|
|
183
|
+
if (options.status) output.print(`Filtered by status: ${options.status}`);
|
|
184
|
+
if (items.length === 0) {
|
|
185
|
+
output.print('No screenshots on this page.');
|
|
186
|
+
}
|
|
187
|
+
for (let [index, item] of items.entries()) {
|
|
188
|
+
let row = start + index + 1;
|
|
189
|
+
let diff = !Number.isFinite(item.diffPercentage) ? '' : ` · ${item.diffPercentage.toFixed(2)}% diff`;
|
|
190
|
+
output.print(`${row}. ${item.status} · ${item.name} · ${item.id}${diff}`);
|
|
191
|
+
}
|
|
192
|
+
if (data.hasNext) {
|
|
193
|
+
output.blank();
|
|
194
|
+
let nextPageCommand = `Next page: vizzly tdd screenshots list --page ${page + 1} ` + `--page-size ${pageSize}`;
|
|
195
|
+
if (options.status) nextPageCommand += ` --status ${options.status}`;
|
|
196
|
+
output.print(nextPageCommand);
|
|
197
|
+
}
|
|
198
|
+
if (items.length > 0) {
|
|
199
|
+
output.blank();
|
|
200
|
+
output.print('Inspect one: vizzly tdd screenshots show <id>');
|
|
201
|
+
}
|
|
202
|
+
return data;
|
|
203
|
+
}
|
|
204
|
+
export function latestTddScreenshot(name = null, workingDir = process.cwd()) {
|
|
205
|
+
let local = readLocalReport(workingDir);
|
|
206
|
+
if (local.error) {
|
|
207
|
+
reportError(local.error);
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
let matches = local.report.comparisons.map((comparison, index) => ({
|
|
211
|
+
comparison,
|
|
212
|
+
index
|
|
213
|
+
})).filter(({
|
|
214
|
+
comparison
|
|
215
|
+
}) => !name || comparison.name === name).sort((left, right) => {
|
|
216
|
+
let leftTimestamp = Number(left.comparison.timestamp) || 0;
|
|
217
|
+
let rightTimestamp = Number(right.comparison.timestamp) || 0;
|
|
218
|
+
return rightTimestamp - leftTimestamp || right.index - left.index;
|
|
219
|
+
});
|
|
220
|
+
if (matches.length === 0) {
|
|
221
|
+
reportError(name ? `No local TDD screenshot found for "${name}".` : 'No local TDD screenshots were captured.');
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
let {
|
|
225
|
+
comparison
|
|
226
|
+
} = matches[0];
|
|
227
|
+
let image = imageReferences(comparison, local.vizzlyDir, ['current']).current;
|
|
228
|
+
let data = {
|
|
229
|
+
id: comparison.id,
|
|
230
|
+
name: comparison.name,
|
|
231
|
+
status: comparison.status,
|
|
232
|
+
timestamp: comparison.timestamp ?? null,
|
|
233
|
+
image
|
|
234
|
+
};
|
|
235
|
+
if (!image?.exists) {
|
|
236
|
+
reportError(`The latest screenshot is missing for "${comparison.name}".`);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
if (output.isJson()) {
|
|
240
|
+
output.data(data);
|
|
241
|
+
return data;
|
|
242
|
+
}
|
|
243
|
+
output.print(image.path);
|
|
244
|
+
return data;
|
|
245
|
+
}
|
|
246
|
+
function findComparison(comparisons, idOrName) {
|
|
247
|
+
let byId = comparisons.find(item => item.id === idOrName);
|
|
248
|
+
if (byId) return {
|
|
249
|
+
comparison: byId
|
|
250
|
+
};
|
|
251
|
+
let bySignature = comparisons.find(item => item.signature === idOrName);
|
|
252
|
+
if (bySignature) return {
|
|
253
|
+
comparison: bySignature
|
|
254
|
+
};
|
|
255
|
+
let byName = comparisons.filter(item => item.name === idOrName);
|
|
256
|
+
if (byName.length === 1) return {
|
|
257
|
+
comparison: byName[0]
|
|
258
|
+
};
|
|
259
|
+
if (byName.length > 1) {
|
|
260
|
+
return {
|
|
261
|
+
error: `More than one screenshot is named "${idOrName}". ` + `Use a comparison ID: ${byName.map(item => item.id).join(', ')}`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
let prefixMatches = comparisons.filter(item => String(item.id || '').startsWith(idOrName));
|
|
265
|
+
if (prefixMatches.length === 1) return {
|
|
266
|
+
comparison: prefixMatches[0]
|
|
267
|
+
};
|
|
268
|
+
if (prefixMatches.length > 1) {
|
|
269
|
+
return {
|
|
270
|
+
error: `Comparison ID prefix "${idOrName}" is ambiguous. Use a full ID.`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
error: `No local TDD screenshot found for "${idOrName}".`
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
export function showTddScreenshot(idOrName, options = {}, workingDir = process.cwd()) {
|
|
278
|
+
let selected = readLocalComparison(idOrName, workingDir);
|
|
279
|
+
if (selected.error) {
|
|
280
|
+
reportError(selected.error);
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
let {
|
|
284
|
+
kinds,
|
|
285
|
+
error
|
|
286
|
+
} = getImageKinds(options.image);
|
|
287
|
+
if (error) {
|
|
288
|
+
reportError(error);
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
let {
|
|
292
|
+
local,
|
|
293
|
+
comparison
|
|
294
|
+
} = selected;
|
|
295
|
+
let comparisonFacts = {
|
|
296
|
+
...comparison
|
|
297
|
+
};
|
|
298
|
+
delete comparisonFacts.baseline;
|
|
299
|
+
delete comparisonFacts.current;
|
|
300
|
+
delete comparisonFacts.diff;
|
|
301
|
+
let detail = {
|
|
302
|
+
comparison: comparisonFacts,
|
|
303
|
+
images: imageReferences(comparison, local.vizzlyDir, kinds),
|
|
304
|
+
details: readComparisonDetails(local.vizzlyDir)[comparison.id] || {}
|
|
305
|
+
};
|
|
306
|
+
if (output.isJson()) {
|
|
307
|
+
output.data(detail);
|
|
308
|
+
return detail;
|
|
309
|
+
}
|
|
310
|
+
let {
|
|
311
|
+
properties = {}
|
|
312
|
+
} = comparison;
|
|
313
|
+
let viewport = getViewport(properties);
|
|
314
|
+
output.print(`${comparison.name} · ${comparison.status}`);
|
|
315
|
+
output.print(`ID: ${comparison.id}`);
|
|
316
|
+
if (comparison.signature) output.print(`Signature: ${comparison.signature}`);
|
|
317
|
+
if (Number.isFinite(comparison.diffPercentage)) {
|
|
318
|
+
output.print(`Difference: ${comparison.diffPercentage.toFixed(2)}%`);
|
|
319
|
+
}
|
|
320
|
+
if (properties.browser) output.print(`Browser: ${properties.browser}`);
|
|
321
|
+
if (viewport.width && viewport.height) {
|
|
322
|
+
output.print(`Viewport: ${viewport.width}×${viewport.height}`);
|
|
323
|
+
}
|
|
324
|
+
if (comparison.error) output.print(`Error: ${comparison.error}`);
|
|
325
|
+
if (comparison.reason) output.print(`Reason: ${comparison.reason}`);
|
|
326
|
+
for (let kind of kinds) {
|
|
327
|
+
let image = detail.images[kind];
|
|
328
|
+
let imageLabel = image ? `${image.path}${image.exists ? '' : ' (missing)'}` : 'not available';
|
|
329
|
+
output.print(`${kind}: ${imageLabel}`);
|
|
330
|
+
}
|
|
331
|
+
return detail;
|
|
332
|
+
}
|
|
333
|
+
function removeComparisonDetails(comparison, vizzlyDir, writeFile) {
|
|
334
|
+
let detailsPath = join(vizzlyDir, 'comparison-details.json');
|
|
335
|
+
if (!existsSync(detailsPath)) return;
|
|
336
|
+
let details;
|
|
337
|
+
try {
|
|
338
|
+
details = JSON.parse(readFileSync(detailsPath, 'utf8'));
|
|
339
|
+
} catch {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (!details || typeof details !== 'object' || Array.isArray(details) || !Object.hasOwn(details, comparison.id)) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
delete details[comparison.id];
|
|
346
|
+
writeFile(detailsPath, JSON.stringify(details));
|
|
347
|
+
}
|
|
348
|
+
function updateReportAfterAccept(report, comparison, baselinePath) {
|
|
349
|
+
let timestamp = Date.now();
|
|
350
|
+
let updatedComparison = {
|
|
351
|
+
...comparison,
|
|
352
|
+
status: 'passed',
|
|
353
|
+
baseline: baselinePath ? `/images/baselines/${basename(baselinePath)}` : comparison.baseline,
|
|
354
|
+
diffPercentage: 0,
|
|
355
|
+
diff: null
|
|
356
|
+
};
|
|
357
|
+
let comparisons = report.comparisons.map(item => item.id === comparison.id ? {
|
|
358
|
+
...updatedComparison,
|
|
359
|
+
initialStatus: item.initialStatus || item.status
|
|
360
|
+
} : item);
|
|
361
|
+
return {
|
|
362
|
+
...report,
|
|
363
|
+
timestamp,
|
|
364
|
+
comparisons,
|
|
365
|
+
summary: {
|
|
366
|
+
total: comparisons.length,
|
|
367
|
+
passed: comparisons.filter(item => ['passed', 'baseline-created', 'new'].includes(item.status)).length,
|
|
368
|
+
failed: comparisons.filter(item => item.status === 'failed').length,
|
|
369
|
+
rejected: comparisons.filter(item => item.status === 'rejected').length,
|
|
370
|
+
errors: comparisons.filter(item => item.status === 'error').length
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
export async function acceptTddScreenshot(idOrName, workingDir = process.cwd(), deps = {}) {
|
|
375
|
+
let {
|
|
376
|
+
loadConfig = defaultLoadConfig,
|
|
377
|
+
TddService = defaultTddService,
|
|
378
|
+
writeFile = writeFileSync,
|
|
379
|
+
output: commandOutput = output
|
|
380
|
+
} = deps;
|
|
381
|
+
let selected = readLocalComparison(idOrName, workingDir);
|
|
382
|
+
if (selected.error) {
|
|
383
|
+
reportError(selected.error, commandOutput);
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
let {
|
|
387
|
+
local,
|
|
388
|
+
comparison
|
|
389
|
+
} = selected;
|
|
390
|
+
try {
|
|
391
|
+
let config = await loadConfig();
|
|
392
|
+
let service = new TddService(config, local.projectDir);
|
|
393
|
+
await service.loadBaseline();
|
|
394
|
+
let accepted = await service.acceptBaseline(comparison);
|
|
395
|
+
let signature = generateScreenshotSignature(comparison.name, comparison.properties || {}, service.signatureProperties);
|
|
396
|
+
let baseline = service.baselineData?.screenshots?.find(item => item.signature === signature);
|
|
397
|
+
writeFile(join(local.vizzlyDir, 'report-data.json'), JSON.stringify(updateReportAfterAccept(local.report, comparison, baseline?.path)));
|
|
398
|
+
removeComparisonDetails(comparison, local.vizzlyDir, writeFile);
|
|
399
|
+
let data = {
|
|
400
|
+
id: comparison.id,
|
|
401
|
+
name: comparison.name,
|
|
402
|
+
status: accepted.status,
|
|
403
|
+
baseline: baseline?.path ?? null
|
|
404
|
+
};
|
|
405
|
+
if (commandOutput.isJson()) {
|
|
406
|
+
commandOutput.data(data);
|
|
407
|
+
} else {
|
|
408
|
+
commandOutput.success(`Accepted ${comparison.name} as the local baseline`, {
|
|
409
|
+
baseline: data.baseline
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return data;
|
|
413
|
+
} catch (error) {
|
|
414
|
+
reportError(`Failed to accept ${comparison.name}: ${error.message}`, commandOutput);
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
package/dist/commands/tdd.js
CHANGED
|
@@ -14,7 +14,7 @@ import { createConfigService as defaultCreateConfigService } from '../services/c
|
|
|
14
14
|
import { createProjectService as defaultCreateProjectService } from '../services/project-service.js';
|
|
15
15
|
import { initializeDaemon as defaultInitializeDaemon, runTests as defaultRunTests } from '../test-runner/index.js';
|
|
16
16
|
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
17
|
-
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
17
|
+
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitAuthor as defaultDetectCommitAuthor, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
18
18
|
import * as defaultOutput from '../utils/output.js';
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -31,7 +31,7 @@ function buildLocalContextCommand({
|
|
|
31
31
|
structured = false
|
|
32
32
|
} = {}) {
|
|
33
33
|
let jsonFlag = structured ? ' --json' : '';
|
|
34
|
-
return `vizzly context build current --source local
|
|
34
|
+
return `vizzly context build current --source local${jsonFlag}`;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/**
|
|
@@ -58,6 +58,7 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {},
|
|
|
58
58
|
runTests = defaultRunTests,
|
|
59
59
|
detectBranch = defaultDetectBranch,
|
|
60
60
|
detectCommit = defaultDetectCommit,
|
|
61
|
+
detectCommitAuthor = defaultDetectCommitAuthor,
|
|
61
62
|
detectCommitMessage = defaultDetectCommitMessage,
|
|
62
63
|
detectPullRequestNumber = defaultDetectPullRequestNumber,
|
|
63
64
|
generateBuildNameWithGit = defaultGenerateBuildNameWithGit,
|
|
@@ -107,6 +108,7 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {},
|
|
|
107
108
|
let branch = await detectBranch(options.branch || config.build.branch);
|
|
108
109
|
let commit = await detectCommit(options.commit || config.build.commit);
|
|
109
110
|
let message = options.message || config.build.message || (await detectCommitMessage());
|
|
111
|
+
let commitAuthor = await detectCommitAuthor();
|
|
110
112
|
let buildName = await generateBuildNameWithGit(options.buildName || configuredBuildName);
|
|
111
113
|
let pullRequestNumber = detectPullRequestNumber();
|
|
112
114
|
|
|
@@ -160,6 +162,8 @@ export async function tddCommand(testCommand, options = {}, globalOptions = {},
|
|
|
160
162
|
branch,
|
|
161
163
|
commit,
|
|
162
164
|
message,
|
|
165
|
+
commitAuthorName: commitAuthor.name,
|
|
166
|
+
commitAuthorEmail: commitAuthor.email,
|
|
163
167
|
environment: config.build.environment,
|
|
164
168
|
pullRequestNumber,
|
|
165
169
|
parallelId: config.parallelId,
|
package/dist/commands/upload.js
CHANGED
|
@@ -3,7 +3,7 @@ import { CONFIG_DEFAULTS } from '../config/core.js';
|
|
|
3
3
|
import { createUploader as defaultCreateUploader } from '../uploader/index.js';
|
|
4
4
|
import { getAppBaseUrl } from '../utils/api-url.js';
|
|
5
5
|
import { loadConfig as defaultLoadConfig } from '../utils/config-loader.js';
|
|
6
|
-
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
6
|
+
import { detectBranch as defaultDetectBranch, detectCommit as defaultDetectCommit, detectCommitAuthor as defaultDetectCommitAuthor, detectCommitMessage as defaultDetectCommitMessage, detectPullRequestNumber as defaultDetectPullRequestNumber, generateBuildNameWithGit as defaultGenerateBuildNameWithGit } from '../utils/git.js';
|
|
7
7
|
import * as defaultOutput from '../utils/output.js';
|
|
8
8
|
import { writeSession as defaultWriteSession } from '../utils/session.js';
|
|
9
9
|
|
|
@@ -59,6 +59,7 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
59
59
|
createUploader = defaultCreateUploader,
|
|
60
60
|
detectBranch = defaultDetectBranch,
|
|
61
61
|
detectCommit = defaultDetectCommit,
|
|
62
|
+
detectCommitAuthor = defaultDetectCommitAuthor,
|
|
62
63
|
detectCommitMessage = defaultDetectCommitMessage,
|
|
63
64
|
detectPullRequestNumber = defaultDetectPullRequestNumber,
|
|
64
65
|
generateBuildNameWithGit = defaultGenerateBuildNameWithGit,
|
|
@@ -100,6 +101,7 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
100
101
|
let branch = await detectBranch(options.branch || config.build.branch);
|
|
101
102
|
let commit = await detectCommit(options.commit || config.build.commit);
|
|
102
103
|
let message = options.message || config.build.message || (await detectCommitMessage());
|
|
104
|
+
let commitAuthor = await detectCommitAuthor();
|
|
103
105
|
let buildName = await generateBuildNameWithGit(options.buildName || configuredBuildName);
|
|
104
106
|
let pullRequestNumber = detectPullRequestNumber();
|
|
105
107
|
output.info(`Uploading screenshots from: ${screenshotsPath}`);
|
|
@@ -127,6 +129,8 @@ export async function uploadCommand(screenshotsPath, options = {}, globalOptions
|
|
|
127
129
|
branch,
|
|
128
130
|
commit,
|
|
129
131
|
message,
|
|
132
|
+
commitAuthorName: commitAuthor.name,
|
|
133
|
+
commitAuthorEmail: commitAuthor.email,
|
|
130
134
|
environment: config.build.environment,
|
|
131
135
|
threshold: config.comparison.threshold,
|
|
132
136
|
minClusterSize: config.comparison.minClusterSize,
|