@percy/core 1.12.0 → 1.13.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/dist/api.js +60 -45
- package/dist/browser.js +82 -68
- package/dist/config.js +16 -9
- package/dist/discovery.js +65 -60
- package/dist/install.js +29 -27
- package/dist/network.js +72 -79
- package/dist/page.js +47 -54
- package/dist/percy.js +85 -85
- package/dist/queue.js +103 -146
- package/dist/server.js +51 -88
- package/dist/session.js +8 -15
- package/dist/snapshot.js +105 -92
- package/dist/utils.js +60 -58
- package/package.json +6 -6
package/dist/snapshot.js
CHANGED
|
@@ -3,45 +3,46 @@ import PercyConfig from '@percy/config';
|
|
|
3
3
|
import micromatch from 'micromatch';
|
|
4
4
|
import { configSchema } from './config.js';
|
|
5
5
|
import Queue from './queue.js';
|
|
6
|
-
import { request, hostnameMatches, yieldTo } from './utils.js';
|
|
6
|
+
import { request, hostnameMatches, yieldTo } from './utils.js';
|
|
7
7
|
|
|
8
|
+
// Throw a better error message for missing or invalid urls
|
|
8
9
|
function validURL(url, base) {
|
|
9
10
|
if (!url) {
|
|
10
11
|
throw new Error('Missing required URL for snapshot');
|
|
11
12
|
}
|
|
12
|
-
|
|
13
13
|
try {
|
|
14
14
|
return new URL(url, base);
|
|
15
15
|
} catch (e) {
|
|
16
16
|
throw new Error(`Invalid snapshot URL: ${e.input}`);
|
|
17
17
|
}
|
|
18
|
-
}
|
|
18
|
+
}
|
|
19
19
|
|
|
20
|
+
// used to deserialize regular expression strings
|
|
21
|
+
const RE_REGEXP = /^\/(.+)\/(\w+)?$/;
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
// Returns true or false if a snapshot matches the provided include and exclude predicates. A
|
|
22
24
|
// predicate can be an array of predicates, a regular expression, a glob pattern, or a function.
|
|
23
|
-
|
|
24
25
|
function snapshotMatches(snapshot, include, exclude) {
|
|
25
26
|
var _include, _include2;
|
|
26
|
-
|
|
27
27
|
// support an options object as the second argument
|
|
28
28
|
if ((_include = include) !== null && _include !== void 0 && _include.include || (_include2 = include) !== null && _include2 !== void 0 && _include2.exclude) ({
|
|
29
29
|
include,
|
|
30
30
|
exclude
|
|
31
|
-
} = include);
|
|
31
|
+
} = include);
|
|
32
32
|
|
|
33
|
+
// recursive predicate test function
|
|
33
34
|
let test = (predicate, fallback) => {
|
|
34
35
|
if (predicate && typeof predicate === 'string') {
|
|
35
36
|
// snapshot name matches exactly or matches a glob
|
|
36
|
-
let result = snapshot.name === predicate || micromatch.isMatch(snapshot.name, predicate);
|
|
37
|
+
let result = snapshot.name === predicate || micromatch.isMatch(snapshot.name, predicate);
|
|
37
38
|
|
|
39
|
+
// snapshot might match a string-based regexp pattern
|
|
38
40
|
if (!result) {
|
|
39
41
|
try {
|
|
40
42
|
let [, parsed, flags] = RE_REGEXP.exec(predicate) || [];
|
|
41
43
|
result = !!parsed && new RegExp(parsed, flags).test(snapshot.name);
|
|
42
44
|
} catch {}
|
|
43
45
|
}
|
|
44
|
-
|
|
45
46
|
return result;
|
|
46
47
|
} else if (predicate instanceof RegExp) {
|
|
47
48
|
// snapshot matches a regular expression
|
|
@@ -56,46 +57,49 @@ function snapshotMatches(snapshot, include, exclude) {
|
|
|
56
57
|
// default fallback
|
|
57
58
|
return fallback;
|
|
58
59
|
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (!include && !exclude) return true; // not excluded or explicitly included
|
|
60
|
+
};
|
|
63
61
|
|
|
62
|
+
// nothing to match, return true
|
|
63
|
+
if (!include && !exclude) return true;
|
|
64
|
+
// not excluded or explicitly included
|
|
64
65
|
return !test(exclude, false) && test(include, true);
|
|
65
|
-
}
|
|
66
|
-
|
|
66
|
+
}
|
|
67
67
|
|
|
68
|
+
// Accepts an array of snapshots to filter and map with matching options.
|
|
68
69
|
function mapSnapshotOptions(snapshots, context) {
|
|
69
|
-
if (!(snapshots !== null && snapshots !== void 0 && snapshots.length)) return [];
|
|
70
|
+
if (!(snapshots !== null && snapshots !== void 0 && snapshots.length)) return [];
|
|
70
71
|
|
|
72
|
+
// reduce options into a single function
|
|
71
73
|
let applyOptions = [].concat((context === null || context === void 0 ? void 0 : context.options) || []).reduceRight((next, {
|
|
72
74
|
include,
|
|
73
75
|
exclude,
|
|
74
76
|
...opts
|
|
75
|
-
}) => snap => next(
|
|
76
|
-
|
|
77
|
+
}) => snap => next(
|
|
78
|
+
// assign additional options to included snaphots
|
|
79
|
+
snapshotMatches(snap, include, exclude) ? Object.assign(snap, opts) : snap), snap => getSnapshotOptions(snap, context));
|
|
77
80
|
|
|
81
|
+
// reduce snapshots with overrides
|
|
78
82
|
return snapshots.reduce((acc, snapshot) => {
|
|
79
83
|
var _snapshot;
|
|
80
|
-
|
|
81
84
|
// transform snapshot URL shorthand into an object
|
|
82
85
|
if (typeof snapshot === 'string') snapshot = {
|
|
83
86
|
url: snapshot
|
|
84
|
-
};
|
|
87
|
+
};
|
|
85
88
|
|
|
89
|
+
// normalize the snapshot url and use it for the default name
|
|
86
90
|
let url = validURL(snapshot.url, context === null || context === void 0 ? void 0 : context.baseUrl);
|
|
87
91
|
(_snapshot = snapshot).name || (_snapshot.name = `${url.pathname}${url.search}${url.hash}`);
|
|
88
|
-
snapshot.url = url.href;
|
|
92
|
+
snapshot.url = url.href;
|
|
89
93
|
|
|
94
|
+
// use the snapshot when matching include/exclude
|
|
90
95
|
if (snapshotMatches(snapshot, context)) {
|
|
91
96
|
acc.push(applyOptions(snapshot));
|
|
92
97
|
}
|
|
93
|
-
|
|
94
98
|
return acc;
|
|
95
99
|
}, []);
|
|
96
|
-
}
|
|
97
|
-
|
|
100
|
+
}
|
|
98
101
|
|
|
102
|
+
// Return snapshot options merged with defaults and global config.
|
|
99
103
|
function getSnapshotOptions(options, {
|
|
100
104
|
config,
|
|
101
105
|
meta
|
|
@@ -105,7 +109,8 @@ function getSnapshotOptions(options, {
|
|
|
105
109
|
discovery: {
|
|
106
110
|
allowedHostnames: [validURL(options.url).hostname]
|
|
107
111
|
},
|
|
108
|
-
meta: {
|
|
112
|
+
meta: {
|
|
113
|
+
...meta,
|
|
109
114
|
snapshot: {
|
|
110
115
|
name: options.name
|
|
111
116
|
}
|
|
@@ -123,26 +128,22 @@ function getSnapshotOptions(options, {
|
|
|
123
128
|
}
|
|
124
129
|
}, options], (path, prev, next) => {
|
|
125
130
|
var _next, _next2;
|
|
126
|
-
|
|
127
131
|
switch (path.map(k => k.toString()).join('.')) {
|
|
128
132
|
case 'widths':
|
|
129
133
|
// dedup, sort, and override widths when not empty
|
|
130
134
|
return [path, !((_next = next) !== null && _next !== void 0 && _next.length) ? prev : [...new Set(next)].sort((a, b) => a - b)];
|
|
131
|
-
|
|
132
135
|
case 'percyCSS':
|
|
133
136
|
// concatenate percy css
|
|
134
137
|
return [path, [prev, next].filter(Boolean).join('\n')];
|
|
135
|
-
|
|
136
138
|
case 'execute':
|
|
137
139
|
// shorthand for execute.beforeSnapshot
|
|
138
140
|
return Array.isArray(next) || typeof next !== 'object' ? [path.concat('beforeSnapshot'), next] : [path];
|
|
139
|
-
|
|
140
141
|
case 'discovery.disallowedHostnames':
|
|
141
142
|
// prevent disallowing the root hostname
|
|
142
143
|
return [path, !((_next2 = next) !== null && _next2 !== void 0 && _next2.length) ? prev : (prev ?? []).concat(next).filter(h => !hostnameMatches(h, options.url))];
|
|
143
|
-
}
|
|
144
|
-
|
|
144
|
+
}
|
|
145
145
|
|
|
146
|
+
// ensure additional snapshots have complete names
|
|
146
147
|
if (path[0] === 'additionalSnapshots' && path.length === 2) {
|
|
147
148
|
let {
|
|
148
149
|
prefix = '',
|
|
@@ -156,14 +157,13 @@ function getSnapshotOptions(options, {
|
|
|
156
157
|
return [path, next];
|
|
157
158
|
}
|
|
158
159
|
});
|
|
159
|
-
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Validates and migrates snapshot options against the correct schema based on provided
|
|
160
163
|
// properties. Eagerly throws an error when missing a URL for any snapshot, and warns about all
|
|
161
164
|
// other invalid options which are also scrubbed from the returned migrated options.
|
|
162
|
-
|
|
163
|
-
|
|
164
165
|
export function validateSnapshotOptions(options) {
|
|
165
166
|
var _migrated$baseUrl;
|
|
166
|
-
|
|
167
167
|
// decide which schema to validate against
|
|
168
168
|
let schema = ['domSnapshot', 'dom-snapshot', 'dom_snapshot'].some(k => k in options) && '/snapshot/dom' || 'url' in options && '/snapshot' || 'sitemap' in options && '/snapshot/sitemap' || 'serve' in options && '/snapshot/server' || 'snapshots' in options && '/snapshot/list' || '/snapshot';
|
|
169
169
|
let {
|
|
@@ -172,85 +172,88 @@ export function validateSnapshotOptions(options) {
|
|
|
172
172
|
environmentInfo,
|
|
173
173
|
snapshots,
|
|
174
174
|
...migrated
|
|
175
|
-
} = PercyConfig.migrate(options, schema);
|
|
175
|
+
} = PercyConfig.migrate(options, schema);
|
|
176
176
|
|
|
177
|
+
// maintain a trailing slash for base URLs to normalize them
|
|
177
178
|
if (((_migrated$baseUrl = migrated.baseUrl) === null || _migrated$baseUrl === void 0 ? void 0 : _migrated$baseUrl.endsWith('/')) === false) migrated.baseUrl += '/';
|
|
178
|
-
let baseUrl = schema === '/snapshot/server' ? 'http://localhost/' : migrated.baseUrl;
|
|
179
|
+
let baseUrl = schema === '/snapshot/server' ? 'http://localhost/' : migrated.baseUrl;
|
|
179
180
|
|
|
181
|
+
// gather info for validating individual snapshot URLs
|
|
180
182
|
let isSnapshot = schema === '/snapshot/dom' || schema === '/snapshot';
|
|
181
183
|
let snaps = isSnapshot ? [migrated] : Array.isArray(snapshots) ? snapshots : [];
|
|
184
|
+
for (let snap of snaps) validURL(typeof snap === 'string' ? snap : snap.url, baseUrl);
|
|
182
185
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
// add back snapshots before validating and scrubbing; function snapshots are validated later
|
|
186
187
|
if (snapshots) migrated.snapshots = typeof snapshots === 'function' ? [] : snapshots;else if (!isSnapshot && options.snapshots) migrated.snapshots = [];
|
|
187
188
|
let errors = PercyConfig.validate(migrated, schema);
|
|
188
|
-
|
|
189
189
|
if (errors) {
|
|
190
190
|
// warn on validation errors
|
|
191
191
|
let log = logger('core:snapshot');
|
|
192
192
|
log.warn('Invalid snapshot options:');
|
|
193
|
-
|
|
194
193
|
for (let e of errors) log.warn(`- ${e.path}: ${e.message}`);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (typeof snapshots === 'function') migrated.snapshots = snapshots; // add back an empty array if all server snapshots were scrubbed
|
|
194
|
+
}
|
|
199
195
|
|
|
196
|
+
// add back the snapshots function if there was one
|
|
197
|
+
if (typeof snapshots === 'function') migrated.snapshots = snapshots;
|
|
198
|
+
// add back an empty array if all server snapshots were scrubbed
|
|
200
199
|
if ('serve' in options && 'snapshots' in options) migrated.snapshots ?? (migrated.snapshots = []);
|
|
201
200
|
return {
|
|
202
201
|
clientInfo,
|
|
203
202
|
environmentInfo,
|
|
204
203
|
...migrated
|
|
205
204
|
};
|
|
206
|
-
}
|
|
207
|
-
// including a trailing slash, are removed from the resulting list.
|
|
205
|
+
}
|
|
208
206
|
|
|
207
|
+
// Fetches a sitemap and parses it into a list of URLs for taking snapshots. Duplicate URLs,
|
|
208
|
+
// including a trailing slash, are removed from the resulting list.
|
|
209
209
|
async function getSitemapSnapshots(options) {
|
|
210
210
|
return request(options.sitemap, (body, res) => {
|
|
211
211
|
// validate sitemap content-type
|
|
212
212
|
let [contentType] = res.headers['content-type'].split(';');
|
|
213
|
-
|
|
214
213
|
if (!/^(application|text)\/xml$/.test(contentType)) {
|
|
215
214
|
throw new Error('The sitemap must be an XML document, ' + `but the content-type was "${contentType}"`);
|
|
216
|
-
}
|
|
217
|
-
|
|
215
|
+
}
|
|
218
216
|
|
|
219
|
-
|
|
217
|
+
// parse XML content into a list of URLs
|
|
218
|
+
let urls = body.match(/(?<=<loc>)(.*?)(?=<\/loc>)/ig) ?? [];
|
|
220
219
|
|
|
220
|
+
// filter out duplicate URLs that differ by a trailing slash
|
|
221
221
|
return urls.filter((url, i) => {
|
|
222
222
|
let match = urls.indexOf(url.replace(/\/$/, ''));
|
|
223
223
|
return match === -1 || match === i;
|
|
224
224
|
});
|
|
225
225
|
});
|
|
226
|
-
}
|
|
227
|
-
|
|
226
|
+
}
|
|
228
227
|
|
|
228
|
+
// Returns an array of derived snapshot options
|
|
229
229
|
export async function* gatherSnapshots(options, context) {
|
|
230
230
|
let {
|
|
231
231
|
baseUrl,
|
|
232
232
|
snapshots
|
|
233
233
|
} = options;
|
|
234
234
|
if ('url' in options) [snapshots, options] = [[options], {}];
|
|
235
|
-
if ('sitemap' in options) snapshots = yield getSitemapSnapshots(options);
|
|
235
|
+
if ('sitemap' in options) snapshots = yield getSitemapSnapshots(options);
|
|
236
236
|
|
|
237
|
+
// validate evaluated snapshots
|
|
237
238
|
if (typeof snapshots === 'function') {
|
|
238
239
|
snapshots = yield* yieldTo(snapshots(baseUrl));
|
|
239
240
|
snapshots = validateSnapshotOptions({
|
|
240
241
|
baseUrl,
|
|
241
242
|
snapshots
|
|
242
243
|
}).snapshots;
|
|
243
|
-
}
|
|
244
|
-
|
|
244
|
+
}
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
// map snapshots with snapshot options
|
|
247
|
+
snapshots = mapSnapshotOptions(snapshots, {
|
|
248
|
+
...options,
|
|
247
249
|
...context
|
|
248
250
|
});
|
|
249
251
|
if (!snapshots.length) throw new Error('No snapshots found');
|
|
250
252
|
return snapshots;
|
|
251
|
-
}
|
|
252
|
-
// resources are deduplicated by widths, and all other resources are deduplicated by their URL.
|
|
253
|
+
}
|
|
253
254
|
|
|
255
|
+
// Merges snapshots and deduplicates resource arrays. Duplicate log resources are replaced, root
|
|
256
|
+
// resources are deduplicated by widths, and all other resources are deduplicated by their URL.
|
|
254
257
|
function mergeSnapshotOptions(prev = {}, next) {
|
|
255
258
|
let {
|
|
256
259
|
resources: oldResources = [],
|
|
@@ -261,37 +264,41 @@ function mergeSnapshotOptions(prev = {}, next) {
|
|
|
261
264
|
widths = [],
|
|
262
265
|
width,
|
|
263
266
|
...incoming
|
|
264
|
-
} = next;
|
|
267
|
+
} = next;
|
|
265
268
|
|
|
266
|
-
|
|
269
|
+
// prioritize singular widths over mutilple widths
|
|
270
|
+
widths = width ? [width] : widths;
|
|
267
271
|
|
|
272
|
+
// deduplicate resources by associated widths and url
|
|
268
273
|
let resources = oldResources.reduce((all, resource) => {
|
|
269
274
|
if (resource.log || resource.widths.every(w => widths.includes(w))) return all;
|
|
270
275
|
if (!resource.root && all.some(r => r.url === resource.url)) return all;
|
|
271
276
|
resource.widths = resource.widths.filter(w => !widths.includes(w));
|
|
272
277
|
return all.concat(resource);
|
|
273
|
-
}, newResources.map(r => ({
|
|
278
|
+
}, newResources.map(r => ({
|
|
279
|
+
...r,
|
|
274
280
|
widths
|
|
275
|
-
})));
|
|
281
|
+
})));
|
|
276
282
|
|
|
283
|
+
// sort resources after merging; roots first by min-width & logs last
|
|
277
284
|
resources.sort((a, b) => {
|
|
278
285
|
if (a.root && b.root) return Math.min(...b.widths) - Math.min(...a.widths);
|
|
279
286
|
return a.root || b.log ? -1 : a.log || b.root ? 1 : 0;
|
|
280
|
-
});
|
|
287
|
+
});
|
|
281
288
|
|
|
289
|
+
// overwrite resources and ensure unique widths
|
|
282
290
|
return PercyConfig.merge([existing, incoming, {
|
|
283
291
|
widths,
|
|
284
292
|
resources
|
|
285
293
|
}], (path, prev, next) => {
|
|
286
294
|
if (path[0] === 'resources') return [path, next];
|
|
287
|
-
|
|
288
295
|
if (path[0] === 'widths' && prev && next) {
|
|
289
296
|
return [path, [...new Set([...prev, ...next])]];
|
|
290
297
|
}
|
|
291
298
|
});
|
|
292
|
-
}
|
|
293
|
-
|
|
299
|
+
}
|
|
294
300
|
|
|
301
|
+
// Creates a snapshots queue that manages a Percy build and uploads snapshots.
|
|
295
302
|
export function createSnapshotsQueue(percy) {
|
|
296
303
|
let {
|
|
297
304
|
concurrency
|
|
@@ -300,7 +307,8 @@ export function createSnapshotsQueue(percy) {
|
|
|
300
307
|
let build;
|
|
301
308
|
return queue.set({
|
|
302
309
|
concurrency
|
|
303
|
-
})
|
|
310
|
+
})
|
|
311
|
+
// on start, create a new Percy build
|
|
304
312
|
.handle('start', async () => {
|
|
305
313
|
try {
|
|
306
314
|
build = percy.build = {};
|
|
@@ -313,8 +321,8 @@ export function createSnapshotsQueue(percy) {
|
|
|
313
321
|
id: data.id,
|
|
314
322
|
url,
|
|
315
323
|
number
|
|
316
|
-
});
|
|
317
|
-
|
|
324
|
+
});
|
|
325
|
+
// immediately run the queue if not delayed or deferred
|
|
318
326
|
if (!percy.delayUploads && !percy.deferUploads) queue.run();
|
|
319
327
|
} catch (err) {
|
|
320
328
|
// immediately throw the error if not delayed or deferred
|
|
@@ -326,12 +334,11 @@ export function createSnapshotsQueue(percy) {
|
|
|
326
334
|
percy.log.error(err);
|
|
327
335
|
queue.close(true);
|
|
328
336
|
}
|
|
329
|
-
})
|
|
337
|
+
})
|
|
338
|
+
// on end, maybe finalize the build and log about build info
|
|
330
339
|
.handle('end', async () => {
|
|
331
340
|
var _build, _build2;
|
|
332
|
-
|
|
333
341
|
if (!percy.readyState) return;
|
|
334
|
-
|
|
335
342
|
if ((_build = build) !== null && _build !== void 0 && _build.failed) {
|
|
336
343
|
percy.log.warn(`Build #${build.number} failed: ${build.url}`, {
|
|
337
344
|
build
|
|
@@ -346,25 +353,30 @@ export function createSnapshotsQueue(percy) {
|
|
|
346
353
|
build
|
|
347
354
|
});
|
|
348
355
|
}
|
|
349
|
-
})
|
|
356
|
+
})
|
|
357
|
+
// snapshots are unique by name alone
|
|
350
358
|
.handle('find', ({
|
|
351
359
|
name
|
|
352
|
-
}, snapshot) => snapshot.name === name)
|
|
360
|
+
}, snapshot) => snapshot.name === name)
|
|
361
|
+
// when pushed, maybe flush old snapshots or possibly merge with existing snapshots
|
|
353
362
|
.handle('push', (snapshot, existing) => {
|
|
354
363
|
let {
|
|
355
364
|
name,
|
|
356
365
|
meta
|
|
357
|
-
} = snapshot;
|
|
366
|
+
} = snapshot;
|
|
358
367
|
|
|
368
|
+
// log immediately when not deferred or dry-running
|
|
359
369
|
if (!percy.deferUploads) percy.log.info(`Snapshot taken: ${name}`, meta);
|
|
360
|
-
if (percy.dryRun) percy.log.info(`Snapshot found: ${name}`, meta);
|
|
361
|
-
|
|
362
|
-
if (percy.delayUploads && !percy.deferUploads) queue.flush(); // overwrite any existing snapshot when not deferred or when resources is a function
|
|
363
|
-
|
|
364
|
-
if (!percy.deferUploads || typeof snapshot.resources === 'function') return snapshot; // merge snapshot options when uploads are deferred
|
|
370
|
+
if (percy.dryRun) percy.log.info(`Snapshot found: ${name}`, meta);
|
|
365
371
|
|
|
372
|
+
// immediately flush when uploads are delayed but not skipped
|
|
373
|
+
if (percy.delayUploads && !percy.deferUploads) queue.flush();
|
|
374
|
+
// overwrite any existing snapshot when not deferred or when resources is a function
|
|
375
|
+
if (!percy.deferUploads || typeof snapshot.resources === 'function') return snapshot;
|
|
376
|
+
// merge snapshot options when uploads are deferred
|
|
366
377
|
return mergeSnapshotOptions(existing, snapshot);
|
|
367
|
-
})
|
|
378
|
+
})
|
|
379
|
+
// send snapshots to be uploaded to the build
|
|
368
380
|
.handle('task', async function* ({
|
|
369
381
|
resources,
|
|
370
382
|
...snapshot
|
|
@@ -372,21 +384,25 @@ export function createSnapshotsQueue(percy) {
|
|
|
372
384
|
let {
|
|
373
385
|
name,
|
|
374
386
|
meta
|
|
375
|
-
} = snapshot;
|
|
387
|
+
} = snapshot;
|
|
376
388
|
|
|
377
|
-
|
|
389
|
+
// yield to evaluated snapshot resources
|
|
390
|
+
snapshot.resources = typeof resources === 'function' ? yield* yieldTo(resources()) : resources;
|
|
378
391
|
|
|
392
|
+
// upload the snapshot and log when deferred
|
|
379
393
|
let send = 'tag' in snapshot ? 'sendComparison' : 'sendSnapshot';
|
|
380
394
|
let response = yield percy.client[send](build.id, snapshot);
|
|
381
395
|
if (percy.deferUploads) percy.log.info(`Snapshot uploaded: ${name}`, meta);
|
|
382
|
-
return {
|
|
396
|
+
return {
|
|
397
|
+
...snapshot,
|
|
383
398
|
response
|
|
384
399
|
};
|
|
385
|
-
})
|
|
400
|
+
})
|
|
401
|
+
// handle possible build errors returned by the API
|
|
386
402
|
.handle('error', (snapshot, error) => {
|
|
387
403
|
var _error$response;
|
|
388
|
-
|
|
389
|
-
|
|
404
|
+
let result = {
|
|
405
|
+
...snapshot,
|
|
390
406
|
error
|
|
391
407
|
};
|
|
392
408
|
let {
|
|
@@ -397,16 +413,13 @@ export function createSnapshotsQueue(percy) {
|
|
|
397
413
|
if (error.name === 'AbortError') return result;
|
|
398
414
|
let failed = ((_error$response = error.response) === null || _error$response === void 0 ? void 0 : _error$response.statusCode) === 422 && error.response.body.errors.find(e => {
|
|
399
415
|
var _e$source;
|
|
400
|
-
|
|
401
416
|
return ((_e$source = e.source) === null || _e$source === void 0 ? void 0 : _e$source.pointer) === '/data/attributes/build';
|
|
402
417
|
});
|
|
403
|
-
|
|
404
418
|
if (failed) {
|
|
405
419
|
build.error = error.message = failed.detail;
|
|
406
420
|
build.failed = true;
|
|
407
421
|
queue.close(true);
|
|
408
422
|
}
|
|
409
|
-
|
|
410
423
|
percy.log.error(`Encountered an error uploading snapshot: ${name}`, meta);
|
|
411
424
|
percy.log.error(error, meta);
|
|
412
425
|
return result;
|