@vizzly-testing/cli 0.7.1 → 0.7.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.
|
@@ -113,29 +113,44 @@ export class ApiService {
|
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* Check if SHAs already exist on the server
|
|
116
|
-
* @param {string[]} shas - Array of SHA256 hashes to check
|
|
116
|
+
* @param {string[]|Object[]} shas - Array of SHA256 hashes to check, or array of screenshot objects with metadata
|
|
117
117
|
* @param {string} buildId - Build ID for screenshot record creation
|
|
118
118
|
* @returns {Promise<Object>} Response with existing SHAs and screenshot data
|
|
119
119
|
*/
|
|
120
120
|
async checkShas(shas, buildId) {
|
|
121
121
|
try {
|
|
122
|
+
let requestBody;
|
|
123
|
+
|
|
124
|
+
// Check if we're using the new signature-based format (array of objects) or legacy format (array of strings)
|
|
125
|
+
if (Array.isArray(shas) && shas.length > 0 && typeof shas[0] === 'object' && shas[0].sha256) {
|
|
126
|
+
// New signature-based format
|
|
127
|
+
requestBody = {
|
|
128
|
+
buildId,
|
|
129
|
+
screenshots: shas
|
|
130
|
+
};
|
|
131
|
+
} else {
|
|
132
|
+
// Legacy SHA-only format
|
|
133
|
+
requestBody = {
|
|
134
|
+
shas,
|
|
135
|
+
buildId
|
|
136
|
+
};
|
|
137
|
+
}
|
|
122
138
|
const response = await this.request('/api/sdk/check-shas', {
|
|
123
139
|
method: 'POST',
|
|
124
140
|
headers: {
|
|
125
141
|
'Content-Type': 'application/json'
|
|
126
142
|
},
|
|
127
|
-
body: JSON.stringify(
|
|
128
|
-
shas,
|
|
129
|
-
buildId
|
|
130
|
-
})
|
|
143
|
+
body: JSON.stringify(requestBody)
|
|
131
144
|
});
|
|
132
145
|
return response;
|
|
133
146
|
} catch (error) {
|
|
134
147
|
// Continue without deduplication on error
|
|
135
148
|
console.debug('SHA check failed, continuing without deduplication:', error.message);
|
|
149
|
+
// Extract SHAs for fallback response regardless of format
|
|
150
|
+
const shaList = Array.isArray(shas) && shas.length > 0 && typeof shas[0] === 'object' ? shas.map(s => s.sha256) : shas;
|
|
136
151
|
return {
|
|
137
152
|
existing: [],
|
|
138
|
-
missing:
|
|
153
|
+
missing: shaList,
|
|
139
154
|
screenshots: []
|
|
140
155
|
};
|
|
141
156
|
}
|
|
@@ -167,13 +182,22 @@ export class ApiService {
|
|
|
167
182
|
});
|
|
168
183
|
}
|
|
169
184
|
|
|
170
|
-
// Normal flow with SHA deduplication
|
|
185
|
+
// Normal flow with SHA deduplication using signature-based format
|
|
171
186
|
const sha256 = crypto.createHash('sha256').update(buffer).digest('hex');
|
|
172
187
|
|
|
173
|
-
//
|
|
174
|
-
const
|
|
188
|
+
// Create screenshot object with signature data for checking
|
|
189
|
+
const screenshotCheck = [{
|
|
190
|
+
sha256,
|
|
191
|
+
name,
|
|
192
|
+
browser: metadata?.browser || 'chrome',
|
|
193
|
+
viewport_width: metadata?.viewport?.width || 1920,
|
|
194
|
+
viewport_height: metadata?.viewport?.height || 1080
|
|
195
|
+
}];
|
|
196
|
+
|
|
197
|
+
// Check if this SHA with signature already exists
|
|
198
|
+
const checkResult = await this.checkShas(screenshotCheck, buildId);
|
|
175
199
|
if (checkResult.existing && checkResult.existing.includes(sha256)) {
|
|
176
|
-
// File already exists, screenshot record was automatically created
|
|
200
|
+
// File already exists with same signature, screenshot record was automatically created
|
|
177
201
|
const screenshot = checkResult.screenshots?.find(s => s.sha256 === sha256);
|
|
178
202
|
return {
|
|
179
203
|
message: 'Screenshot already exists, skipped upload',
|
|
@@ -184,7 +208,7 @@ export class ApiService {
|
|
|
184
208
|
};
|
|
185
209
|
}
|
|
186
210
|
|
|
187
|
-
// File doesn't exist, proceed with upload
|
|
211
|
+
// File doesn't exist or has different signature, proceed with upload
|
|
188
212
|
return this.request(`/api/sdk/builds/${buildId}/screenshots`, {
|
|
189
213
|
method: 'POST',
|
|
190
214
|
headers: {
|
|
@@ -274,29 +274,31 @@ async function processFiles(files, signal, onProgress) {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
/**
|
|
277
|
-
* Check which files already exist on the server
|
|
277
|
+
* Check which files already exist on the server using signature-based deduplication
|
|
278
278
|
*/
|
|
279
279
|
async function checkExistingFiles(fileMetadata, api, signal, buildId) {
|
|
280
|
-
const allShas = fileMetadata.map(f => f.sha256);
|
|
281
280
|
const existingShas = new Set();
|
|
282
281
|
const allScreenshots = [];
|
|
283
282
|
|
|
284
|
-
// Check in batches
|
|
285
|
-
for (let i = 0; i <
|
|
283
|
+
// Check in batches using the new signature-based format
|
|
284
|
+
for (let i = 0; i < fileMetadata.length; i += DEFAULT_SHA_CHECK_BATCH_SIZE) {
|
|
286
285
|
if (signal.aborted) throw new UploadError('Operation cancelled');
|
|
287
|
-
const batch =
|
|
286
|
+
const batch = fileMetadata.slice(i, i + DEFAULT_SHA_CHECK_BATCH_SIZE);
|
|
287
|
+
|
|
288
|
+
// Convert file metadata to screenshot objects with signature data
|
|
289
|
+
const screenshotBatch = batch.map(file => ({
|
|
290
|
+
sha256: file.sha256,
|
|
291
|
+
name: file.filename.replace(/\.png$/, ''),
|
|
292
|
+
// Remove .png extension for name
|
|
293
|
+
// Extract browser from filename if available (e.g., "homepage-chrome.png" -> "chrome")
|
|
294
|
+
browser: extractBrowserFromFilename(file.filename) || 'chrome',
|
|
295
|
+
// Default to chrome
|
|
296
|
+
// Default viewport dimensions (these could be extracted from filename or metadata if available)
|
|
297
|
+
viewport_width: 1920,
|
|
298
|
+
viewport_height: 1080
|
|
299
|
+
}));
|
|
288
300
|
try {
|
|
289
|
-
const res = await api.
|
|
290
|
-
method: 'POST',
|
|
291
|
-
headers: {
|
|
292
|
-
'Content-Type': 'application/json'
|
|
293
|
-
},
|
|
294
|
-
body: JSON.stringify({
|
|
295
|
-
shas: batch,
|
|
296
|
-
buildId
|
|
297
|
-
}),
|
|
298
|
-
signal
|
|
299
|
-
});
|
|
301
|
+
const res = await api.checkShas(screenshotBatch, buildId);
|
|
300
302
|
const {
|
|
301
303
|
existing = [],
|
|
302
304
|
screenshots = []
|
|
@@ -315,6 +317,22 @@ async function checkExistingFiles(fileMetadata, api, signal, buildId) {
|
|
|
315
317
|
};
|
|
316
318
|
}
|
|
317
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Extract browser name from filename
|
|
322
|
+
* @param {string} filename - The screenshot filename
|
|
323
|
+
* @returns {string|null} Browser name or null if not found
|
|
324
|
+
*/
|
|
325
|
+
function extractBrowserFromFilename(filename) {
|
|
326
|
+
const browsers = ['chrome', 'firefox', 'safari', 'edge', 'webkit'];
|
|
327
|
+
const lowerFilename = filename.toLowerCase();
|
|
328
|
+
for (const browser of browsers) {
|
|
329
|
+
if (lowerFilename.includes(browser)) {
|
|
330
|
+
return browser;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
318
336
|
/**
|
|
319
337
|
* Upload files to Vizzly
|
|
320
338
|
*/
|
|
@@ -41,11 +41,11 @@ export class ApiService {
|
|
|
41
41
|
createBuild(metadata: any): Promise<any>;
|
|
42
42
|
/**
|
|
43
43
|
* Check if SHAs already exist on the server
|
|
44
|
-
* @param {string[]} shas - Array of SHA256 hashes to check
|
|
44
|
+
* @param {string[]|Object[]} shas - Array of SHA256 hashes to check, or array of screenshot objects with metadata
|
|
45
45
|
* @param {string} buildId - Build ID for screenshot record creation
|
|
46
46
|
* @returns {Promise<Object>} Response with existing SHAs and screenshot data
|
|
47
47
|
*/
|
|
48
|
-
checkShas(shas: string[], buildId: string): Promise<any>;
|
|
48
|
+
checkShas(shas: string[] | any[], buildId: string): Promise<any>;
|
|
49
49
|
/**
|
|
50
50
|
* Upload a screenshot with SHA checking
|
|
51
51
|
* @param {string} buildId - Build ID
|