@percy/core 1.10.3 → 1.11.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 +42 -4
- package/dist/config.js +106 -46
- package/dist/discovery.js +288 -126
- package/dist/network.js +165 -54
- package/dist/page.js +18 -9
- package/dist/percy.js +156 -279
- package/dist/queue.js +335 -99
- package/dist/snapshot.js +243 -277
- package/dist/utils.js +4 -1
- package/package.json +6 -6
package/dist/percy.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import PercyClient from '@percy/client';
|
|
2
2
|
import PercyConfig from '@percy/config';
|
|
3
3
|
import logger from '@percy/logger';
|
|
4
|
-
import Queue from './queue.js';
|
|
5
4
|
import Browser from './browser.js';
|
|
6
5
|
import { createPercyServer, createStaticServer } from './api.js';
|
|
7
|
-
import { gatherSnapshots,
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
// finalized until all snapshots have been handled.
|
|
6
|
+
import { gatherSnapshots, createSnapshotsQueue, validateSnapshotOptions } from './snapshot.js';
|
|
7
|
+
import { discoverSnapshotResources, createDiscoveryQueue } from './discovery.js';
|
|
8
|
+
import { generatePromise, yieldAll, yieldTo } from './utils.js'; // A Percy instance will create a new build when started, handle snapshot creation, asset discovery,
|
|
9
|
+
// and resource uploads, and will finalize the build when stopped. Snapshots are processed
|
|
10
|
+
// concurrently and the build is not finalized until all snapshots have been handled.
|
|
12
11
|
|
|
13
12
|
export class Percy {
|
|
14
13
|
log = logger('core');
|
|
15
14
|
readyState = null;
|
|
16
|
-
#
|
|
17
|
-
#snapshots =
|
|
15
|
+
#discovery = null;
|
|
16
|
+
#snapshots = null; // Static shortcut to create and start an instance in one call
|
|
18
17
|
|
|
19
18
|
static async start(options) {
|
|
20
19
|
let instance = new this(options);
|
|
@@ -38,7 +37,7 @@ export class Percy {
|
|
|
38
37
|
// implies `dryRun`, silent logs, and adds extra api endpoints
|
|
39
38
|
testing,
|
|
40
39
|
// configuration filepath
|
|
41
|
-
config,
|
|
40
|
+
config: configFile,
|
|
42
41
|
// provided to @percy/client
|
|
43
42
|
token,
|
|
44
43
|
clientInfo = '',
|
|
@@ -50,6 +49,15 @@ export class Percy {
|
|
|
50
49
|
// options which will become accessible via the `.config` property
|
|
51
50
|
...options
|
|
52
51
|
} = {}) {
|
|
52
|
+
let {
|
|
53
|
+
percy,
|
|
54
|
+
...config
|
|
55
|
+
} = PercyConfig.load({
|
|
56
|
+
overrides: options,
|
|
57
|
+
path: configFile
|
|
58
|
+
});
|
|
59
|
+
deferUploads ?? (deferUploads = percy === null || percy === void 0 ? void 0 : percy.deferUploads);
|
|
60
|
+
this.config = config;
|
|
53
61
|
if (testing) loglevel = 'silent';
|
|
54
62
|
if (loglevel) this.loglevel(loglevel);
|
|
55
63
|
this.testing = testing ? {} : null;
|
|
@@ -57,30 +65,18 @@ export class Percy {
|
|
|
57
65
|
this.skipUploads = this.dryRun || !!skipUploads;
|
|
58
66
|
this.skipDiscovery = this.dryRun || !!skipDiscovery;
|
|
59
67
|
this.delayUploads = this.skipUploads || !!delayUploads;
|
|
60
|
-
this.deferUploads = this.
|
|
61
|
-
if (this.deferUploads) this.#uploads.stop();
|
|
62
|
-
this.config = PercyConfig.load({
|
|
63
|
-
overrides: options,
|
|
64
|
-
path: config
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (this.config.discovery.concurrency) {
|
|
68
|
-
let {
|
|
69
|
-
concurrency
|
|
70
|
-
} = this.config.discovery;
|
|
71
|
-
this.#uploads.concurrency = concurrency;
|
|
72
|
-
this.#snapshots.concurrency = concurrency;
|
|
73
|
-
}
|
|
74
|
-
|
|
68
|
+
this.deferUploads = this.skipUploads || !!deferUploads;
|
|
75
69
|
this.client = new PercyClient({
|
|
76
70
|
token,
|
|
77
71
|
clientInfo,
|
|
78
72
|
environmentInfo
|
|
79
73
|
});
|
|
80
74
|
if (server) this.server = createPercyServer(this, port);
|
|
81
|
-
this.browser = new Browser(this);
|
|
75
|
+
this.browser = new Browser(this);
|
|
76
|
+
this.#discovery = createDiscoveryQueue(this);
|
|
77
|
+
this.#snapshots = createSnapshotsQueue(this); // generator methods are wrapped to autorun and return promises
|
|
82
78
|
|
|
83
|
-
for (let m of ['start', 'stop', 'flush', 'idle', 'snapshot']) {
|
|
79
|
+
for (let m of ['start', 'stop', 'flush', 'idle', 'snapshot', 'upload']) {
|
|
84
80
|
// the original generator can be referenced with percy.yield.<method>
|
|
85
81
|
let method = (this.yield || (this.yield = {}))[m] = this[m].bind(this);
|
|
86
82
|
|
|
@@ -101,7 +97,7 @@ export class Percy {
|
|
|
101
97
|
} // Set client & environment info, and override loaded config options
|
|
102
98
|
|
|
103
99
|
|
|
104
|
-
|
|
100
|
+
set({
|
|
105
101
|
clientInfo,
|
|
106
102
|
environmentInfo,
|
|
107
103
|
...config
|
|
@@ -126,96 +122,45 @@ export class Percy {
|
|
|
126
122
|
this.config = PercyConfig.merge([this.config, config], (path, prev, next) => {
|
|
127
123
|
// replace arrays instead of merging
|
|
128
124
|
return Array.isArray(next) && [path, next];
|
|
129
|
-
}); // adjust concurrency
|
|
130
|
-
|
|
131
|
-
if (this.config.discovery.concurrency) {
|
|
132
|
-
let {
|
|
133
|
-
concurrency
|
|
134
|
-
} = this.config.discovery;
|
|
135
|
-
this.#uploads.concurrency = concurrency;
|
|
136
|
-
this.#snapshots.concurrency = concurrency;
|
|
137
|
-
}
|
|
125
|
+
}); // adjust queue concurrency
|
|
138
126
|
|
|
127
|
+
let {
|
|
128
|
+
concurrency
|
|
129
|
+
} = this.config.discovery;
|
|
130
|
+
this.#discovery.set({
|
|
131
|
+
concurrency
|
|
132
|
+
});
|
|
133
|
+
this.#snapshots.set({
|
|
134
|
+
concurrency
|
|
135
|
+
});
|
|
139
136
|
return this.config;
|
|
140
|
-
} //
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
async *idle() {
|
|
144
|
-
yield* this.#snapshots.idle();
|
|
145
|
-
yield* this.#uploads.idle();
|
|
146
|
-
} // Immediately stops all queues, preventing any more tasks from running
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
close() {
|
|
150
|
-
this.#snapshots.close(true);
|
|
151
|
-
this.#uploads.close(true);
|
|
152
|
-
} // Starts a local API server, a browser process, and queues creating a new Percy build which will run
|
|
153
|
-
// at a later time when uploads are deferred, or run immediately when not deferred.
|
|
137
|
+
} // Starts a local API server, a browser process, and internal queues.
|
|
154
138
|
|
|
155
139
|
|
|
156
140
|
async *start() {
|
|
157
141
|
// already starting or started
|
|
158
142
|
if (this.readyState != null) return;
|
|
159
|
-
this.readyState = 0;
|
|
160
|
-
|
|
161
|
-
let buildTask = this.#uploads.push('build/create', () => {
|
|
162
|
-
// pause other queued tasks until after the build is created
|
|
163
|
-
this.#uploads.stop();
|
|
164
|
-
this.build = {};
|
|
165
|
-
return this.client.createBuild().then(({
|
|
166
|
-
data: {
|
|
167
|
-
id,
|
|
168
|
-
attributes
|
|
169
|
-
}
|
|
170
|
-
}) => {
|
|
171
|
-
let url = attributes['web-url'];
|
|
172
|
-
let number = attributes['build-number'];
|
|
173
|
-
Object.assign(this.build, {
|
|
174
|
-
id,
|
|
175
|
-
url,
|
|
176
|
-
number
|
|
177
|
-
});
|
|
178
|
-
if (!this.delayUploads) this.#uploads.run();
|
|
179
|
-
});
|
|
180
|
-
}, 0); // handle deferred build errors
|
|
181
|
-
|
|
182
|
-
if (this.deferUploads) {
|
|
183
|
-
buildTask.catch(err => {
|
|
184
|
-
this.build = {
|
|
185
|
-
error: 'Failed to create build'
|
|
186
|
-
};
|
|
187
|
-
this.log.error(this.build.error);
|
|
188
|
-
this.log.error(err);
|
|
189
|
-
this.close();
|
|
190
|
-
});
|
|
191
|
-
}
|
|
143
|
+
this.readyState = 0;
|
|
192
144
|
|
|
193
145
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// when not deferred, wait until the build is created first
|
|
197
|
-
if (!this.deferUploads) await buildTask; // maybe launch the discovery browser
|
|
146
|
+
// start the snapshots queue immediately when not delayed or deferred
|
|
147
|
+
if (!this.delayUploads && !this.deferUploads) yield this.#snapshots.start(); // do not start the discovery queue when not needed
|
|
198
148
|
|
|
199
|
-
if (!this.skipDiscovery) yield this.
|
|
149
|
+
if (!this.skipDiscovery) yield this.#discovery.start(); // start a local API server for SDK communication
|
|
200
150
|
|
|
201
|
-
|
|
151
|
+
if (this.server) yield this.server.listen(); // log and mark this instance as started
|
|
202
152
|
|
|
203
153
|
this.log.info('Percy has started!');
|
|
204
154
|
this.readyState = 1;
|
|
205
155
|
} catch (error) {
|
|
206
|
-
var _this$
|
|
207
|
-
|
|
208
|
-
// on error, close any running server and browser
|
|
209
|
-
await ((_this$server3 = this.server) === null || _this$server3 === void 0 ? void 0 : _this$server3.close());
|
|
210
|
-
await this.browser.close(); // mark instance as closed
|
|
211
|
-
|
|
212
|
-
this.readyState = 3; // when uploads are deferred, cancel build creation on abort
|
|
156
|
+
var _this$server2;
|
|
213
157
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
158
|
+
// on error, close any running server and end queues
|
|
159
|
+
await ((_this$server2 = this.server) === null || _this$server2 === void 0 ? void 0 : _this$server2.close());
|
|
160
|
+
await this.#discovery.end();
|
|
161
|
+
await this.#snapshots.end(); // mark this instance as closed unless aborting
|
|
218
162
|
|
|
163
|
+
this.readyState = error.name !== 'AbortError' ? 3 : null; // throw an easier-to-understand error when the port is in use
|
|
219
164
|
|
|
220
165
|
if (error.code === 'EADDRINUSE') {
|
|
221
166
|
throw new Error('Percy is already running or the port is in use');
|
|
@@ -223,53 +168,36 @@ export class Percy {
|
|
|
223
168
|
throw error;
|
|
224
169
|
}
|
|
225
170
|
}
|
|
226
|
-
} //
|
|
171
|
+
} // Resolves once snapshot and upload queues are idle
|
|
227
172
|
|
|
228
173
|
|
|
229
|
-
async *
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (this.#snapshots.size) {
|
|
235
|
-
if (close) this.#snapshots.close();
|
|
236
|
-
yield* this.#snapshots.flush(s => {
|
|
237
|
-
// do not log a count when not closing or if asset discovery is disabled
|
|
238
|
-
if (!close || this.skipDiscovery) return;
|
|
239
|
-
this.log.progress(`Processing ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s);
|
|
240
|
-
});
|
|
241
|
-
} // run, close, and wait for the upload queue to empty
|
|
174
|
+
async *idle() {
|
|
175
|
+
yield* this.#discovery.idle();
|
|
176
|
+
yield* this.#snapshots.idle();
|
|
177
|
+
} // Wait for currently queued snapshots then run and wait for resulting uploads
|
|
242
178
|
|
|
243
179
|
|
|
244
|
-
|
|
245
|
-
|
|
180
|
+
async *flush(options) {
|
|
181
|
+
if (!this.readyState || this.readyState > 2) return;
|
|
182
|
+
let callback = typeof options === 'function' ? options : null;
|
|
183
|
+
options && (options = !callback ? [].concat(options) : null); // wait until the next event loop for synchronous snapshots
|
|
246
184
|
|
|
247
|
-
|
|
248
|
-
if (this.build && !this.build.id) yield* this.#uploads.idle();
|
|
249
|
-
yield* this.#uploads.flush(s => {
|
|
250
|
-
// do not log a count when not closing or while creating a build
|
|
251
|
-
if (!close || this.#uploads.has('build/create')) return;
|
|
252
|
-
this.log.progress(`Uploading ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s);
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
} catch (error) {
|
|
257
|
-
// reopen closed queues when aborted
|
|
185
|
+
yield new Promise(r => setImmediate(r)); // flush and log progress for discovery before snapshots
|
|
258
186
|
|
|
259
|
-
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
this.#uploads.open();
|
|
263
|
-
}
|
|
187
|
+
if (!this.skipDiscovery && this.#discovery.size) {
|
|
188
|
+
if (options) yield* yieldAll(options.map(o => this.#discovery.process(o)));else yield* this.#discovery.flush(size => callback === null || callback === void 0 ? void 0 : callback('Processing', size));
|
|
189
|
+
} // flush and log progress for snapshot uploads
|
|
264
190
|
|
|
265
|
-
|
|
191
|
+
|
|
192
|
+
if (!this.skipUploads && this.#snapshots.size) {
|
|
193
|
+
if (options) yield* yieldAll(options.map(o => this.#snapshots.process(o)));else yield* this.#snapshots.flush(size => callback === null || callback === void 0 ? void 0 : callback('Uploading', size));
|
|
266
194
|
}
|
|
267
|
-
} // Stops the local API server and
|
|
268
|
-
//
|
|
195
|
+
} // Stops the local API server and closes the browser and internal queues once snapshots have
|
|
196
|
+
// completed. Does nothing if not running. When `force` is true, any queued snapshots are cleared.
|
|
269
197
|
|
|
270
198
|
|
|
271
199
|
async *stop(force) {
|
|
272
|
-
var _this$
|
|
200
|
+
var _this$server3;
|
|
273
201
|
|
|
274
202
|
// not started, but the browser was launched
|
|
275
203
|
if (!this.readyState && this.browser.isConnected()) {
|
|
@@ -279,16 +207,24 @@ export class Percy {
|
|
|
279
207
|
|
|
280
208
|
if (!this.readyState || this.readyState > 2) return; // close queues asap
|
|
281
209
|
|
|
282
|
-
if (force)
|
|
210
|
+
if (force) {
|
|
211
|
+
this.#discovery.close(true);
|
|
212
|
+
this.#snapshots.close(true);
|
|
213
|
+
} // already stopping
|
|
214
|
+
|
|
283
215
|
|
|
284
216
|
if (this.readyState === 2) return;
|
|
285
217
|
this.readyState = 2; // log when force stopping
|
|
286
218
|
|
|
287
|
-
if (force) this.log.info('Stopping percy...');
|
|
219
|
+
if (force) this.log.info('Stopping percy...'); // used to log snapshot count information
|
|
220
|
+
|
|
221
|
+
let info = (state, size) => `${state} ` + `${size} snapshot${size !== 1 ? 's' : ''}`;
|
|
288
222
|
|
|
289
223
|
try {
|
|
290
|
-
//
|
|
291
|
-
yield* this.yield.flush(
|
|
224
|
+
// flush discovery and snapshot queues
|
|
225
|
+
yield* this.yield.flush((state, size) => {
|
|
226
|
+
this.log.progress(`${info(state, size)}...`, !!size);
|
|
227
|
+
});
|
|
292
228
|
} catch (error) {
|
|
293
229
|
// reset ready state when aborted
|
|
294
230
|
|
|
@@ -298,81 +234,32 @@ export class Percy {
|
|
|
298
234
|
} // if dry-running, log the total number of snapshots
|
|
299
235
|
|
|
300
236
|
|
|
301
|
-
if (this.dryRun && this.#
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
this.log.info(`Found ${total} snapshot${total !== 1 ? 's' : ''}`);
|
|
305
|
-
} // close any running server and browser
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
await ((_this$server4 = this.server) === null || _this$server4 === void 0 ? void 0 : _this$server4.close());
|
|
309
|
-
await this.browser.close(); // finalize and log build info
|
|
237
|
+
if (this.dryRun && this.#snapshots.size) {
|
|
238
|
+
this.log.info(info('Found', this.#snapshots.size));
|
|
239
|
+
} // close server and end queues
|
|
310
240
|
|
|
311
|
-
let meta = {
|
|
312
|
-
build: this.build
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
if ((_this$build = this.build) !== null && _this$build !== void 0 && _this$build.failed) {
|
|
316
|
-
// do not finalize failed builds
|
|
317
|
-
this.log.warn(`Build #${this.build.number} failed: ${this.build.url}`, meta);
|
|
318
|
-
} else if ((_this$build2 = this.build) !== null && _this$build2 !== void 0 && _this$build2.id) {
|
|
319
|
-
// finalize the build
|
|
320
|
-
await this.client.finalizeBuild(this.build.id);
|
|
321
|
-
this.log.info(`Finalized build #${this.build.number}: ${this.build.url}`, meta);
|
|
322
|
-
} else {
|
|
323
|
-
// no build was ever created (likely failed while deferred)
|
|
324
|
-
this.log.warn('Build not created', meta);
|
|
325
|
-
} // mark instance as stopped
|
|
326
241
|
|
|
242
|
+
await ((_this$server3 = this.server) === null || _this$server3 === void 0 ? void 0 : _this$server3.close());
|
|
243
|
+
await this.#discovery.end();
|
|
244
|
+
await this.#snapshots.end(); // mark instance as stopped
|
|
327
245
|
|
|
328
246
|
this.readyState = 3;
|
|
329
|
-
} //
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
capture(options) {
|
|
333
|
-
this.log.deprecated('The #capture() method will be ' + 'removed in 1.0.0. Use #snapshot() instead.');
|
|
334
|
-
return this.snapshot(options);
|
|
335
|
-
} // Takes one or more snapshots of a page while discovering resources to upload with the
|
|
336
|
-
// snapshot. Once asset discovery has completed, the queued snapshot will resolve and an upload
|
|
337
|
-
// task will be queued separately. Accepts several different syntaxes for taking snapshots using
|
|
338
|
-
// various methods.
|
|
339
|
-
//
|
|
340
|
-
// snapshot(url|{url}|[...url|{url}])
|
|
341
|
-
// - requires fully qualified resolvable urls
|
|
342
|
-
// - snapshot options may be provided with the object syntax
|
|
343
|
-
//
|
|
344
|
-
// snapshot({snapshots:[...url|{url}]})
|
|
345
|
-
// - optional `baseUrl` prepended to snapshot urls
|
|
346
|
-
// - optional `options` apply to all or specific snapshots
|
|
347
|
-
//
|
|
348
|
-
// snapshot(sitemap|{sitemap})
|
|
349
|
-
// - required to be a fully qualified resolvable url ending in `.xml`
|
|
350
|
-
// - optional `include`/`exclude` to filter snapshots
|
|
351
|
-
// - optional `options` apply to all or specific snapshots
|
|
352
|
-
//
|
|
353
|
-
// snapshot({serve})
|
|
354
|
-
// - server address is prepended to snapshot urls
|
|
355
|
-
// - optional `baseUrl` used when serving pages
|
|
356
|
-
// - optional `rewrites`/`cleanUrls` to control snapshot urls
|
|
357
|
-
// - optional `include`/`exclude` to filter snapshots
|
|
358
|
-
// - optional `snapshots`, with fallback to built-in sitemap.xml
|
|
359
|
-
// - optional `options` apply to all or specific snapshots
|
|
360
|
-
//
|
|
361
|
-
// All available syntaxes will eventually push snapshots to the snapshot queue without the need to
|
|
362
|
-
// await on this method directly. This method resolves after the snapshot upload is queued, but
|
|
363
|
-
// does not await on the upload to complete.
|
|
247
|
+
} // Takes one or more snapshots of a page while discovering resources to upload with the resulting
|
|
248
|
+
// snapshots. Once asset discovery has completed for the provided snapshots, the queued task will
|
|
249
|
+
// resolve and an upload task will be queued separately.
|
|
364
250
|
|
|
365
251
|
|
|
366
252
|
snapshot(options) {
|
|
367
|
-
var _this$
|
|
253
|
+
var _this$build;
|
|
368
254
|
|
|
369
255
|
if (this.readyState !== 1) {
|
|
370
256
|
throw new Error('Not running');
|
|
371
|
-
} else if ((_this$
|
|
257
|
+
} else if ((_this$build = this.build) !== null && _this$build !== void 0 && _this$build.error) {
|
|
372
258
|
throw new Error(this.build.error);
|
|
373
259
|
} else if (Array.isArray(options)) {
|
|
374
260
|
return yieldAll(options.map(o => this.yield.snapshot(o)));
|
|
375
|
-
}
|
|
261
|
+
} // accept a url for a sitemap or snapshot
|
|
262
|
+
|
|
376
263
|
|
|
377
264
|
if (typeof options === 'string') {
|
|
378
265
|
options = options.endsWith('.xml') ? {
|
|
@@ -385,104 +272,94 @@ export class Percy {
|
|
|
385
272
|
|
|
386
273
|
options = validateSnapshotOptions(options);
|
|
387
274
|
this.client.addClientInfo(options.clientInfo);
|
|
388
|
-
this.client.addEnvironmentInfo(options.environmentInfo); //
|
|
275
|
+
this.client.addEnvironmentInfo(options.environmentInfo); // without a discovery browser, capture is not possible
|
|
276
|
+
|
|
277
|
+
if (this.skipDiscovery && !this.dryRun && !options.domSnapshot) {
|
|
278
|
+
throw new Error('Cannot capture DOM snapshots when asset discovery is disabled');
|
|
279
|
+
} // return an async generator to allow cancelation
|
|
280
|
+
|
|
389
281
|
|
|
390
282
|
return async function* () {
|
|
391
|
-
let server
|
|
283
|
+
let server;
|
|
392
284
|
|
|
393
285
|
try {
|
|
394
|
-
if (
|
|
395
|
-
//
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
286
|
+
if ('serve' in options) {
|
|
287
|
+
// create and start a static server
|
|
288
|
+
let {
|
|
289
|
+
baseUrl,
|
|
290
|
+
snapshots
|
|
291
|
+
} = options;
|
|
292
|
+
server = yield createStaticServer(options).listen();
|
|
293
|
+
baseUrl = options.baseUrl = new URL(baseUrl || '', server.address()).href;
|
|
294
|
+
if (!snapshots) options.sitemap = new URL('sitemap.xml', baseUrl).href;
|
|
295
|
+
} // gather snapshots and discover snapshot resources
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
yield* discoverSnapshotResources(this.#discovery, {
|
|
299
|
+
skipDiscovery: this.skipDiscovery,
|
|
300
|
+
dryRun: this.dryRun,
|
|
301
|
+
snapshots: yield* gatherSnapshots(options, {
|
|
302
|
+
meta: {
|
|
303
|
+
build: this.build
|
|
304
|
+
},
|
|
305
|
+
config: this.config
|
|
306
|
+
})
|
|
307
|
+
}, snapshot => {
|
|
308
|
+
// push each finished snapshot to the snapshots queue
|
|
309
|
+
this.#snapshots.push(snapshot);
|
|
310
|
+
});
|
|
411
311
|
} finally {
|
|
412
|
-
|
|
312
|
+
var _server;
|
|
313
|
+
|
|
314
|
+
// always close any created server
|
|
315
|
+
await ((_server = server) === null || _server === void 0 ? void 0 : _server.close());
|
|
413
316
|
}
|
|
414
317
|
}.call(this);
|
|
415
|
-
} //
|
|
416
|
-
|
|
318
|
+
} // Uploads one or more snapshots directly to the current Percy build
|
|
417
319
|
|
|
418
|
-
_cancelSnapshot(snapshot) {
|
|
419
|
-
this.#snapshots.cancel(`snapshot/${snapshot.name}`);
|
|
420
320
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
321
|
+
upload(options) {
|
|
322
|
+
if (this.readyState !== 1) {
|
|
323
|
+
throw new Error('Not running');
|
|
324
|
+
} else if (Array.isArray(options)) {
|
|
325
|
+
return yieldAll(options.map(o => this.yield.upload(o)));
|
|
326
|
+
} // validate comparison uploads and warn about any errors
|
|
427
327
|
|
|
428
328
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
this._cancelSnapshot(snapshot);
|
|
329
|
+
if ('tag' in options || 'tiles' in options) {
|
|
330
|
+
var _options$tag;
|
|
432
331
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (!this.dryRun) this.log.info(`Snapshot taken: ${snap.name}`, snap.meta);
|
|
332
|
+
// throw when missing required snapshot or tag name
|
|
333
|
+
if (!options.name) throw new Error('Missing required snapshot name');
|
|
334
|
+
if (!((_options$tag = options.tag) !== null && _options$tag !== void 0 && _options$tag.name)) throw new Error('Missing required tag name for comparison'); // normalize, migrate, and remove certain properties from validating
|
|
437
335
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
} else {
|
|
446
|
-
this.log.error(`Encountered an error taking snapshot: ${snapshot.name}`, snapshot.meta);
|
|
447
|
-
this.log.error(error, snapshot.meta);
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}.bind(this));
|
|
451
|
-
} // Queues a snapshot upload with the provided options
|
|
336
|
+
options = PercyConfig.migrate(options, '/comparison');
|
|
337
|
+
let {
|
|
338
|
+
clientInfo,
|
|
339
|
+
environmentInfo,
|
|
340
|
+
...comparison
|
|
341
|
+
} = options;
|
|
342
|
+
let errors = PercyConfig.validate(comparison, '/comparison');
|
|
452
343
|
|
|
344
|
+
if (errors) {
|
|
345
|
+
this.log.warn('Invalid upload options:');
|
|
453
346
|
|
|
454
|
-
|
|
455
|
-
|
|
347
|
+
for (let e of errors) this.log.warn(`- ${e.path}: ${e.message}`);
|
|
348
|
+
}
|
|
349
|
+
} // add client & environment info
|
|
456
350
|
|
|
457
|
-
if ((_this$build4 = this.build) !== null && _this$build4 !== void 0 && _this$build4.error) throw new Error(this.build.error); // maybe process any existing delayed uploads
|
|
458
351
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
// when delayed, stop the queue before other uploads are processed
|
|
462
|
-
if (this.readyState < 2 && this.delayUploads) this.#uploads.stop();
|
|
352
|
+
this.client.addClientInfo(options.clientInfo);
|
|
353
|
+
this.client.addEnvironmentInfo(options.environmentInfo); // return an async generator to allow cancelation
|
|
463
354
|
|
|
355
|
+
return async function* () {
|
|
464
356
|
try {
|
|
465
|
-
|
|
466
|
-
if (typeof options === 'function') options = await options();
|
|
467
|
-
await this.client.sendSnapshot(this.build.id, options);
|
|
357
|
+
return yield* yieldTo(this.#snapshots.push(options));
|
|
468
358
|
} catch (error) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
let failed = ((_error$response = error.response) === null || _error$response === void 0 ? void 0 : _error$response.statusCode) === 422 && error.response.body.errors.find(e => {
|
|
472
|
-
var _e$source;
|
|
473
|
-
|
|
474
|
-
return ((_e$source = e.source) === null || _e$source === void 0 ? void 0 : _e$source.pointer) === '/data/attributes/build';
|
|
475
|
-
});
|
|
476
|
-
this.log.error(`Encountered an error uploading snapshot: ${name}`, options.meta);
|
|
477
|
-
this.log.error((failed === null || failed === void 0 ? void 0 : failed.detail) ?? error, options.meta); // build failed at some point, stop accepting snapshots
|
|
478
|
-
|
|
479
|
-
if (failed) {
|
|
480
|
-
this.build.error = failed.detail;
|
|
481
|
-
this.build.failed = true;
|
|
482
|
-
this.close();
|
|
483
|
-
}
|
|
359
|
+
this.#snapshots.cancel(options);
|
|
360
|
+
throw error;
|
|
484
361
|
}
|
|
485
|
-
});
|
|
362
|
+
}.call(this);
|
|
486
363
|
}
|
|
487
364
|
|
|
488
365
|
}
|