@percy/core 1.0.0-beta.71 → 1.0.0-beta.75
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/browser.js +29 -25
- package/dist/config.js +5 -4
- package/dist/index.js +8 -6
- package/dist/network.js +6 -3
- package/dist/page.js +16 -6
- package/dist/percy.js +200 -150
- package/dist/queue.js +49 -37
- package/dist/server.js +12 -5
- package/dist/session.js +4 -2
- package/dist/snapshot.js +14 -9
- package/dist/utils.js +91 -27
- package/package.json +14 -14
- package/test/helpers/server.js +5 -1
package/dist/browser.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.default = exports.Browser = void 0;
|
|
7
7
|
|
|
8
8
|
var _os = _interopRequireDefault(require("os"));
|
|
9
9
|
|
|
@@ -63,6 +63,8 @@ class Browser extends _events.default {
|
|
|
63
63
|
|
|
64
64
|
_defineProperty(this, "sessions", new Map());
|
|
65
65
|
|
|
66
|
+
_defineProperty(this, "readyState", null);
|
|
67
|
+
|
|
66
68
|
_defineProperty(this, "closed", false);
|
|
67
69
|
|
|
68
70
|
_classPrivateFieldInitSpec(this, _callbacks, {
|
|
@@ -78,7 +80,8 @@ class Browser extends _events.default {
|
|
|
78
80
|
_defineProperty(this, "args", [// disable the translate popup
|
|
79
81
|
'--disable-features=Translate', // disable several subsystems which run network requests in the background
|
|
80
82
|
'--disable-background-networking', // disable task throttling of timer tasks from background pages
|
|
81
|
-
'--disable-background-timer-throttling', // disable backgrounding
|
|
83
|
+
'--disable-background-timer-throttling', // disable backgrounding renderer processes
|
|
84
|
+
'--disable-renderer-backgrounding', // disable backgrounding renderers for occluded windows (reduce nondeterminism)
|
|
82
85
|
'--disable-backgrounding-occluded-windows', // disable crash reporting
|
|
83
86
|
'--disable-breakpad', // disable client side phishing detection
|
|
84
87
|
'--disable-client-side-phishing-detection', // disable default component extensions with background pages for performance
|
|
@@ -116,7 +119,9 @@ class Browser extends _events.default {
|
|
|
116
119
|
}
|
|
117
120
|
|
|
118
121
|
async launch() {
|
|
119
|
-
|
|
122
|
+
// already launching or launched
|
|
123
|
+
if (this.readyState != null) return;
|
|
124
|
+
this.readyState = 0; // check if any provided executable exists
|
|
120
125
|
|
|
121
126
|
if (this.executable && !(0, _fs.existsSync)(this.executable)) {
|
|
122
127
|
this.log.error(`Browser executable not found: ${this.executable}`);
|
|
@@ -126,11 +131,10 @@ class Browser extends _events.default {
|
|
|
126
131
|
|
|
127
132
|
this.executable || (this.executable = await _install.default.chromium()); // create a temporary profile directory
|
|
128
133
|
|
|
129
|
-
this.profile = await _fs.promises.mkdtemp(_path.default.join(_os.default.tmpdir(), 'percy-browser-')); //
|
|
130
|
-
|
|
131
|
-
let args = [...this.args, `--user-data-dir=${this.profile}`];
|
|
132
|
-
this.log.debug('Launching browser'); // spawn the browser process detached in its own group and session
|
|
134
|
+
this.profile = await _fs.promises.mkdtemp(_path.default.join(_os.default.tmpdir(), 'percy-browser-')); // spawn the browser process detached in its own group and session
|
|
133
135
|
|
|
136
|
+
let args = this.args.concat(`--user-data-dir=${this.profile}`);
|
|
137
|
+
this.log.debug('Launching browser');
|
|
134
138
|
this.process = (0, _crossSpawn.default)(this.executable, args, {
|
|
135
139
|
detached: process.platform !== 'win32'
|
|
136
140
|
}); // connect a websocket to the devtools address
|
|
@@ -144,7 +148,8 @@ class Browser extends _events.default {
|
|
|
144
148
|
this.ws.on('message', data => this._handleMessage(data)); // get version information
|
|
145
149
|
|
|
146
150
|
this.version = await this.send('Browser.getVersion');
|
|
147
|
-
this.log.debug(`Browser connected: ${this.version.product}`);
|
|
151
|
+
this.log.debug(`Browser connected [${this.process.pid}]: ${this.version.product}`);
|
|
152
|
+
this.readyState = 1;
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
isConnected() {
|
|
@@ -156,7 +161,9 @@ class Browser extends _events.default {
|
|
|
156
161
|
async close() {
|
|
157
162
|
var _this$process4, _this$ws2;
|
|
158
163
|
|
|
164
|
+
// not running, already closed, or closing
|
|
159
165
|
if (this._closed) return this._closed;
|
|
166
|
+
this.readyState = 2;
|
|
160
167
|
this.log.debug('Closing browser'); // resolves when the browser has closed
|
|
161
168
|
|
|
162
169
|
this._closed = Promise.all([new Promise(resolve => {
|
|
@@ -184,8 +191,9 @@ class Browser extends _events.default {
|
|
|
184
191
|
this.log.debug(error);
|
|
185
192
|
});
|
|
186
193
|
}
|
|
187
|
-
|
|
194
|
+
}).then(() => {
|
|
188
195
|
this.log.debug('Browser closed');
|
|
196
|
+
this.readyState = 3;
|
|
189
197
|
}); // reject any pending callbacks
|
|
190
198
|
|
|
191
199
|
for (let callback of _classPrivateFieldGet(this, _callbacks).values()) {
|
|
@@ -282,34 +290,28 @@ class Browser extends _events.default {
|
|
|
282
290
|
let match = chunk.match(/^DevTools listening on (ws:\/\/.*)$/m);
|
|
283
291
|
if (match) cleanup(() => resolve(match[1]));
|
|
284
292
|
};
|
|
285
|
-
/* istanbul ignore next: for sanity */
|
|
286
|
-
|
|
287
293
|
|
|
288
|
-
let
|
|
294
|
+
let handleExitClose = () => handleError();
|
|
289
295
|
|
|
290
|
-
let
|
|
296
|
+
let handleError = error => cleanup(() => {
|
|
297
|
+
var _error$message;
|
|
291
298
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
var _error$message;
|
|
295
|
-
|
|
296
|
-
return reject(new Error(`Failed to launch browser. ${(_error$message = error === null || error === void 0 ? void 0 : error.message) !== null && _error$message !== void 0 ? _error$message : ''}\n${stderr}'\n\n`));
|
|
297
|
-
});
|
|
298
|
-
};
|
|
299
|
+
return reject(new Error(`Failed to launch browser. ${(_error$message = error === null || error === void 0 ? void 0 : error.message) !== null && _error$message !== void 0 ? _error$message : ''}\n${stderr}'\n\n`));
|
|
300
|
+
});
|
|
299
301
|
|
|
300
302
|
let cleanup = callback => {
|
|
301
303
|
clearTimeout(timeoutId);
|
|
302
304
|
this.process.stderr.off('data', handleData);
|
|
303
|
-
this.process.stderr.off('close',
|
|
304
|
-
this.process.off('exit',
|
|
305
|
+
this.process.stderr.off('close', handleExitClose);
|
|
306
|
+
this.process.off('exit', handleExitClose);
|
|
305
307
|
this.process.off('error', handleError);
|
|
306
308
|
callback();
|
|
307
309
|
};
|
|
308
310
|
|
|
309
311
|
let timeoutId = setTimeout(() => handleError(new Error(`Timed out after ${timeout}ms`)), timeout);
|
|
310
312
|
this.process.stderr.on('data', handleData);
|
|
311
|
-
this.process.stderr.on('close',
|
|
312
|
-
this.process.on('exit',
|
|
313
|
+
this.process.stderr.on('close', handleExitClose);
|
|
314
|
+
this.process.on('exit', handleExitClose);
|
|
313
315
|
this.process.on('error', handleError);
|
|
314
316
|
}));
|
|
315
317
|
return this._address;
|
|
@@ -356,4 +358,6 @@ class Browser extends _events.default {
|
|
|
356
358
|
|
|
357
359
|
}
|
|
358
360
|
|
|
359
|
-
exports.
|
|
361
|
+
exports.Browser = Browser;
|
|
362
|
+
var _default = Browser;
|
|
363
|
+
exports.default = _default;
|
package/dist/config.js
CHANGED
|
@@ -296,9 +296,12 @@ const snapshotDOMSchema = {
|
|
|
296
296
|
// schemas have no concept of inheritance, but we can leverage JS for brevity
|
|
297
297
|
...snapshotSchema.properties
|
|
298
298
|
}
|
|
299
|
-
}; //
|
|
299
|
+
}; // Grouped schemas for easier registration
|
|
300
300
|
|
|
301
301
|
exports.snapshotDOMSchema = snapshotDOMSchema;
|
|
302
|
+
const schemas = [configSchema, snapshotSchema, snapshotDOMSchema]; // Config migrate function
|
|
303
|
+
|
|
304
|
+
exports.schemas = schemas;
|
|
302
305
|
|
|
303
306
|
function configMigration(config, util) {
|
|
304
307
|
/* eslint-disable curly */
|
|
@@ -348,10 +351,8 @@ function snapshotMigration(config, util) {
|
|
|
348
351
|
map: 'additionalSnapshots',
|
|
349
352
|
...notice
|
|
350
353
|
});
|
|
351
|
-
} //
|
|
354
|
+
} // Grouped migrations for easier registration
|
|
352
355
|
|
|
353
356
|
|
|
354
|
-
const schemas = [configSchema, snapshotSchema, snapshotDOMSchema];
|
|
355
|
-
exports.schemas = schemas;
|
|
356
357
|
const migrations = [['/config', configMigration], ['/snapshot', snapshotMigration]];
|
|
357
358
|
exports.migrations = migrations;
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const {
|
|
5
|
-
default: PercyConfig
|
|
6
|
-
} = require('@percy/config');
|
|
3
|
+
const PercyConfig = require('@percy/config');
|
|
7
4
|
|
|
8
5
|
const CoreConfig = require('./config');
|
|
9
6
|
|
|
7
|
+
const {
|
|
8
|
+
Percy
|
|
9
|
+
} = require('./percy');
|
|
10
|
+
|
|
10
11
|
PercyConfig.addSchema(CoreConfig.schemas);
|
|
11
|
-
PercyConfig.addMigration(CoreConfig.migrations); //
|
|
12
|
+
PercyConfig.addMigration(CoreConfig.migrations); // export the Percy class with commonjs compatibility
|
|
12
13
|
|
|
13
|
-
module.exports =
|
|
14
|
+
module.exports = Percy;
|
|
15
|
+
module.exports.Percy = Percy;
|
package/dist/network.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.default = exports.Network = void 0;
|
|
7
7
|
|
|
8
8
|
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
9
9
|
|
|
@@ -357,6 +357,9 @@ class Network {
|
|
|
357
357
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
|
-
exports.
|
|
360
|
+
exports.Network = Network;
|
|
361
361
|
|
|
362
|
-
_defineProperty(Network, "TIMEOUT", 30000);
|
|
362
|
+
_defineProperty(Network, "TIMEOUT", 30000);
|
|
363
|
+
|
|
364
|
+
var _default = Network;
|
|
365
|
+
exports.default = _default;
|
package/dist/page.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.default = exports.Page = void 0;
|
|
7
7
|
|
|
8
8
|
var _fs = require("fs");
|
|
9
9
|
|
|
@@ -179,7 +179,14 @@ class Page {
|
|
|
179
179
|
} // wrap the function body with percy helpers
|
|
180
180
|
|
|
181
181
|
|
|
182
|
-
fnbody = 'function withPercyHelpers() {' +
|
|
182
|
+
fnbody = 'function withPercyHelpers() {\n' + [`return (${fnbody})({ generatePromise, waitFor }, ...arguments);`, `${_utils.generatePromise}`, `${_utils.waitFor}`].join('\n\n') + '}';
|
|
183
|
+
/* istanbul ignore else: ironic. */
|
|
184
|
+
|
|
185
|
+
if (fnbody.includes('cov_')) {
|
|
186
|
+
// remove coverage statements during testing
|
|
187
|
+
fnbody = fnbody.replace(/cov_.*?(;\n?|,)\s*/g, '');
|
|
188
|
+
} // send the call function command
|
|
189
|
+
|
|
183
190
|
|
|
184
191
|
let {
|
|
185
192
|
result,
|
|
@@ -214,7 +221,7 @@ class Page {
|
|
|
214
221
|
|
|
215
222
|
for (let script of scripts) {
|
|
216
223
|
if (typeof script === 'string') {
|
|
217
|
-
script = `async eval(
|
|
224
|
+
script = `async eval() {\n${script}\n}`;
|
|
218
225
|
}
|
|
219
226
|
|
|
220
227
|
await this.eval(script);
|
|
@@ -250,7 +257,7 @@ class Page {
|
|
|
250
257
|
} // execute any javascript
|
|
251
258
|
|
|
252
259
|
|
|
253
|
-
await this.evaluate(typeof execute === '
|
|
260
|
+
await this.evaluate(typeof execute === 'object' && !Array.isArray(execute) ? execute.beforeSnapshot : execute); // wait for any final network activity before capturing the dom snapshot
|
|
254
261
|
|
|
255
262
|
await this.network.idle(); // inject @percy/dom for serialization by evaluating the file contents which adds a global
|
|
256
263
|
// PercyDOM object that we can later check against
|
|
@@ -278,6 +285,9 @@ class Page {
|
|
|
278
285
|
|
|
279
286
|
}
|
|
280
287
|
|
|
281
|
-
exports.
|
|
288
|
+
exports.Page = Page;
|
|
289
|
+
|
|
290
|
+
_defineProperty(Page, "TIMEOUT", 30000);
|
|
282
291
|
|
|
283
|
-
|
|
292
|
+
var _default = Page;
|
|
293
|
+
exports.default = _default;
|
package/dist/percy.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.default = exports.Percy = void 0;
|
|
7
7
|
|
|
8
8
|
var _client = _interopRequireDefault(require("@percy/client"));
|
|
9
9
|
|
|
@@ -21,6 +21,8 @@ var _server = _interopRequireDefault(require("./server"));
|
|
|
21
21
|
|
|
22
22
|
var _snapshot = require("./snapshot");
|
|
23
23
|
|
|
24
|
+
var _utils2 = require("./utils");
|
|
25
|
+
|
|
24
26
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
27
|
|
|
26
28
|
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
@@ -71,7 +73,7 @@ class Percy {
|
|
|
71
73
|
port = 5338,
|
|
72
74
|
// options such as `snapshot` and `discovery` that are valid Percy config
|
|
73
75
|
// options which will become accessible via the `.config` property
|
|
74
|
-
...
|
|
76
|
+
..._options
|
|
75
77
|
} = {}) {
|
|
76
78
|
_defineProperty(this, "log", (0, _logger.default)('core'));
|
|
77
79
|
|
|
@@ -87,19 +89,182 @@ class Percy {
|
|
|
87
89
|
value: new _queue.default()
|
|
88
90
|
});
|
|
89
91
|
|
|
92
|
+
_defineProperty(this, "idle", () => (0, _utils2.generatePromise)(async function* () {
|
|
93
|
+
yield* _classPrivateFieldGet(this, _snapshots).idle();
|
|
94
|
+
yield* _classPrivateFieldGet(this, _uploads).idle();
|
|
95
|
+
}.bind(this)));
|
|
96
|
+
|
|
97
|
+
_defineProperty(this, "start", options => (0, _utils2.generatePromise)(async function* () {
|
|
98
|
+
// already starting or started
|
|
99
|
+
if (this.readyState != null) return;
|
|
100
|
+
this.readyState = 0; // create a percy build as the first immediately queued task
|
|
101
|
+
|
|
102
|
+
let buildTask = _classPrivateFieldGet(this, _uploads).push('build/create', () => {
|
|
103
|
+
// pause other queued tasks until after the build is created
|
|
104
|
+
_classPrivateFieldGet(this, _uploads).stop();
|
|
105
|
+
|
|
106
|
+
return this.client.createBuild().then(({
|
|
107
|
+
data: {
|
|
108
|
+
id,
|
|
109
|
+
attributes
|
|
110
|
+
}
|
|
111
|
+
}) => {
|
|
112
|
+
this.build = {
|
|
113
|
+
id
|
|
114
|
+
};
|
|
115
|
+
this.build.number = attributes['build-number'];
|
|
116
|
+
this.build.url = attributes['web-url'];
|
|
117
|
+
|
|
118
|
+
_classPrivateFieldGet(this, _uploads).run();
|
|
119
|
+
});
|
|
120
|
+
}, 0); // handle deferred build errors
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if (this.deferUploads) {
|
|
124
|
+
buildTask.catch(err => {
|
|
125
|
+
this.log.error('Failed to create build');
|
|
126
|
+
this.log.error(err);
|
|
127
|
+
this.close();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
var _this$server;
|
|
133
|
+
|
|
134
|
+
// when not deferred, wait until the build is created first
|
|
135
|
+
if (!this.deferUploads) await buildTask; // maybe launch the discovery browser
|
|
136
|
+
|
|
137
|
+
if (!this.dryRun && (options === null || options === void 0 ? void 0 : options.browser) !== false) {
|
|
138
|
+
yield this.browser.launch();
|
|
139
|
+
} // start the server after everything else is ready
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
yield (_this$server = this.server) === null || _this$server === void 0 ? void 0 : _this$server.listen(this.port); // mark instance as started
|
|
143
|
+
|
|
144
|
+
this.log.info('Percy has started!');
|
|
145
|
+
this.readyState = 1;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
var _this$server2;
|
|
148
|
+
|
|
149
|
+
// on error, close any running server and browser
|
|
150
|
+
await ((_this$server2 = this.server) === null || _this$server2 === void 0 ? void 0 : _this$server2.close());
|
|
151
|
+
await this.browser.close(); // mark instance as closed
|
|
152
|
+
|
|
153
|
+
this.readyState = 3; // when uploads are deferred, cancel build creation
|
|
154
|
+
|
|
155
|
+
if (error.canceled && this.deferUploads) {
|
|
156
|
+
_classPrivateFieldGet(this, _uploads).cancel('build/create');
|
|
157
|
+
|
|
158
|
+
this.readyState = null;
|
|
159
|
+
} // throw an easier-to-understand error when the port is taken
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
if (error.code === 'EADDRINUSE') {
|
|
163
|
+
throw new Error('Percy is already running or the port is in use');
|
|
164
|
+
} else {
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}.bind(this)));
|
|
169
|
+
|
|
170
|
+
_defineProperty(this, "flush", close => (0, _utils2.generatePromise)(async function* () {
|
|
171
|
+
// close the snapshot queue and wait for it to empty
|
|
172
|
+
if (_classPrivateFieldGet(this, _snapshots).size) {
|
|
173
|
+
if (close) _classPrivateFieldGet(this, _snapshots).close();
|
|
174
|
+
yield* _classPrivateFieldGet(this, _snapshots).flush(s => {
|
|
175
|
+
// do not log a count when not closing or while dry-running
|
|
176
|
+
if (!close || this.dryRun) return;
|
|
177
|
+
this.log.progress(`Processing ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s);
|
|
178
|
+
});
|
|
179
|
+
} // run, close, and wait for the upload queue to empty
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
if (!this.skipUploads && _classPrivateFieldGet(this, _uploads).size) {
|
|
183
|
+
if (close) _classPrivateFieldGet(this, _uploads).close();
|
|
184
|
+
yield* _classPrivateFieldGet(this, _uploads).flush(s => {
|
|
185
|
+
// do not log a count when not closing or while creating a build
|
|
186
|
+
if (!close || _classPrivateFieldGet(this, _uploads).has('build/create')) return;
|
|
187
|
+
this.log.progress(`Uploading ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}.bind(this)).canceled(() => {
|
|
191
|
+
// reopen closed queues when canceled
|
|
192
|
+
_classPrivateFieldGet(this, _snapshots).open();
|
|
193
|
+
|
|
194
|
+
_classPrivateFieldGet(this, _uploads).open();
|
|
195
|
+
}));
|
|
196
|
+
|
|
197
|
+
_defineProperty(this, "stop", force => (0, _utils2.generatePromise)(async function* () {
|
|
198
|
+
var _this$server3, _this$build;
|
|
199
|
+
|
|
200
|
+
// not started, but the browser was launched
|
|
201
|
+
if (!this.readyState && this.browser.isConnected()) {
|
|
202
|
+
await this.browser.close();
|
|
203
|
+
} // not started or already stopped
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if (!this.readyState || this.readyState > 2) return; // close queues asap
|
|
207
|
+
|
|
208
|
+
if (force) this.close(); // already stopping
|
|
209
|
+
|
|
210
|
+
if (this.readyState === 2) return;
|
|
211
|
+
this.readyState = 2; // log when force stopping
|
|
212
|
+
|
|
213
|
+
if (force) this.log.info('Stopping percy...'); // process uploads and close queues
|
|
214
|
+
|
|
215
|
+
yield* this.flush(true); // if dry-running, log the total number of snapshots
|
|
216
|
+
|
|
217
|
+
if (this.dryRun && _classPrivateFieldGet(this, _uploads).size) {
|
|
218
|
+
let total = _classPrivateFieldGet(this, _uploads).size - 1; // subtract the build task
|
|
219
|
+
|
|
220
|
+
this.log.info(`Found ${total} snapshot${total !== 1 ? 's' : ''}`);
|
|
221
|
+
} // close any running server and browser
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
await ((_this$server3 = this.server) === null || _this$server3 === void 0 ? void 0 : _this$server3.close());
|
|
225
|
+
await this.browser.close(); // finalize and log build info
|
|
226
|
+
|
|
227
|
+
let meta = {
|
|
228
|
+
build: this.build
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
if ((_this$build = this.build) !== null && _this$build !== void 0 && _this$build.failed) {
|
|
232
|
+
// do not finalize failed builds
|
|
233
|
+
this.log.warn(`Build #${this.build.number} failed: ${this.build.url}`, meta);
|
|
234
|
+
} else if (this.build) {
|
|
235
|
+
// finalize the build
|
|
236
|
+
await this.client.finalizeBuild(this.build.id);
|
|
237
|
+
this.log.info(`Finalized build #${this.build.number}: ${this.build.url}`, meta);
|
|
238
|
+
} else {
|
|
239
|
+
// no build was ever created (likely failed while deferred)
|
|
240
|
+
this.log.warn('Build not created', meta);
|
|
241
|
+
} // mark instance as stopped
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
this.readyState = 3;
|
|
245
|
+
}.bind(this)).canceled(() => {
|
|
246
|
+
// reset ready state when canceled
|
|
247
|
+
this.readyState = 1;
|
|
248
|
+
}));
|
|
249
|
+
|
|
90
250
|
if (loglevel) this.loglevel(loglevel);
|
|
91
251
|
this.dryRun = !!dryRun;
|
|
92
252
|
this.skipUploads = this.dryRun || !!skipUploads;
|
|
93
253
|
this.deferUploads = this.skipUploads || !!deferUploads;
|
|
254
|
+
if (this.deferUploads) _classPrivateFieldGet(this, _uploads).stop();
|
|
94
255
|
this.config = _config.default.load({
|
|
95
|
-
overrides:
|
|
256
|
+
overrides: _options,
|
|
96
257
|
path: config
|
|
97
258
|
});
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
259
|
+
|
|
260
|
+
if (this.config.discovery.concurrency) {
|
|
261
|
+
let {
|
|
262
|
+
concurrency
|
|
263
|
+
} = this.config.discovery;
|
|
264
|
+
_classPrivateFieldGet(this, _uploads).concurrency = concurrency;
|
|
265
|
+
_classPrivateFieldGet(this, _snapshots).concurrency = concurrency;
|
|
266
|
+
}
|
|
267
|
+
|
|
103
268
|
this.client = new _client.default({
|
|
104
269
|
token,
|
|
105
270
|
clientInfo,
|
|
@@ -151,23 +316,21 @@ class Percy {
|
|
|
151
316
|
this.config = (0, _utils.merge)([this.config, config], (path, prev, next) => {
|
|
152
317
|
// replace arrays instead of merging
|
|
153
318
|
return Array.isArray(next) && [path, next];
|
|
154
|
-
});
|
|
319
|
+
}); // adjust concurrency if necessary
|
|
320
|
+
|
|
321
|
+
if (this.config.discovery.concurrency) {
|
|
322
|
+
let {
|
|
323
|
+
concurrency
|
|
324
|
+
} = this.config.discovery;
|
|
325
|
+
_classPrivateFieldGet(this, _uploads).concurrency = concurrency;
|
|
326
|
+
_classPrivateFieldGet(this, _snapshots).concurrency = concurrency;
|
|
327
|
+
}
|
|
328
|
+
|
|
155
329
|
return this.config;
|
|
156
330
|
} // Resolves once snapshot and upload queues are idle
|
|
157
331
|
|
|
158
332
|
|
|
159
|
-
|
|
160
|
-
await _classPrivateFieldGet(this, _snapshots).idle();
|
|
161
|
-
await _classPrivateFieldGet(this, _uploads).idle();
|
|
162
|
-
} // Waits for snapshot idle and flushes the upload queue
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
async dispatch() {
|
|
166
|
-
await _classPrivateFieldGet(this, _snapshots).idle();
|
|
167
|
-
if (!this.skipUploads) await _classPrivateFieldGet(this, _uploads).flush();
|
|
168
|
-
} // Immediately stops all queues, preventing any more tasks from running
|
|
169
|
-
|
|
170
|
-
|
|
333
|
+
// Immediately stops all queues, preventing any more tasks from running
|
|
171
334
|
close() {
|
|
172
335
|
_classPrivateFieldGet(this, _snapshots).close(true);
|
|
173
336
|
|
|
@@ -176,124 +339,7 @@ class Percy {
|
|
|
176
339
|
// at a later time when uploads are deferred, or run immediately when not deferred.
|
|
177
340
|
|
|
178
341
|
|
|
179
|
-
|
|
180
|
-
// already starting or started
|
|
181
|
-
if (this.readyState != null) return;
|
|
182
|
-
this.readyState = 0; // create a percy build as the first immediately queued task
|
|
183
|
-
|
|
184
|
-
let buildTask = _classPrivateFieldGet(this, _uploads).push('build/create', () => {
|
|
185
|
-
// pause other queued tasks until after the build is created
|
|
186
|
-
_classPrivateFieldGet(this, _uploads).stop();
|
|
187
|
-
|
|
188
|
-
return this.client.createBuild().then(({
|
|
189
|
-
data: {
|
|
190
|
-
id,
|
|
191
|
-
attributes
|
|
192
|
-
}
|
|
193
|
-
}) => {
|
|
194
|
-
this.build = {
|
|
195
|
-
id
|
|
196
|
-
};
|
|
197
|
-
this.build.number = attributes['build-number'];
|
|
198
|
-
this.build.url = attributes['web-url'];
|
|
199
|
-
|
|
200
|
-
_classPrivateFieldGet(this, _uploads).run();
|
|
201
|
-
});
|
|
202
|
-
}, 0); // handle deferred build errors
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (this.deferUploads) {
|
|
206
|
-
buildTask.catch(err => {
|
|
207
|
-
this.log.error('Failed to create build');
|
|
208
|
-
this.log.error(err);
|
|
209
|
-
this.close();
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
try {
|
|
214
|
-
var _this$server;
|
|
215
|
-
|
|
216
|
-
// when not deferred, wait until the build is created first
|
|
217
|
-
if (!this.deferUploads) await buildTask; // launch the discovery browser
|
|
218
|
-
|
|
219
|
-
if (!this.dryRun) await this.browser.launch(); // if there is a server, start listening
|
|
220
|
-
|
|
221
|
-
await ((_this$server = this.server) === null || _this$server === void 0 ? void 0 : _this$server.listen(this.port)); // mark this process as running
|
|
222
|
-
|
|
223
|
-
this.log.info('Percy has started!');
|
|
224
|
-
this.readyState = 1;
|
|
225
|
-
} catch (error) {
|
|
226
|
-
var _this$server2;
|
|
227
|
-
|
|
228
|
-
// on error, close any running server and browser
|
|
229
|
-
await ((_this$server2 = this.server) === null || _this$server2 === void 0 ? void 0 : _this$server2.close());
|
|
230
|
-
await this.browser.close();
|
|
231
|
-
this.readyState = 3; // throw an easier-to-understand error when the port is taken
|
|
232
|
-
|
|
233
|
-
if (error.code === 'EADDRINUSE') {
|
|
234
|
-
throw new Error('Percy is already running or the port is in use');
|
|
235
|
-
} else {
|
|
236
|
-
throw error;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
} // Stops the local API server and browser once snapshots have completed and finalizes the Percy
|
|
240
|
-
// build. Does nothing if not running. When `force` is true, any queued tasks are cleared.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
async stop(force) {
|
|
244
|
-
var _this$server3, _this$build;
|
|
245
|
-
|
|
246
|
-
// not started or already stopped
|
|
247
|
-
if (!this.readyState || this.readyState > 2) return; // close queues asap
|
|
248
|
-
|
|
249
|
-
if (force) this.close(); // already stopping
|
|
250
|
-
|
|
251
|
-
if (this.readyState === 2) return;
|
|
252
|
-
this.readyState = 2; // log when force stopping
|
|
253
|
-
|
|
254
|
-
let meta = {
|
|
255
|
-
build: this.build
|
|
256
|
-
};
|
|
257
|
-
if (force) this.log.info('Stopping percy...', meta); // close the snapshot queue and wait for it to empty
|
|
258
|
-
|
|
259
|
-
if (_classPrivateFieldGet(this, _snapshots).close().size) {
|
|
260
|
-
await _classPrivateFieldGet(this, _snapshots).empty(s => !this.dryRun && this.log.progress(`Processing ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s));
|
|
261
|
-
} // run, close, and wait for the upload queue to empty
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if (!this.skipUploads && _classPrivateFieldGet(this, _uploads).run().close().size) {
|
|
265
|
-
await _classPrivateFieldGet(this, _uploads).empty(s => {
|
|
266
|
-
this.log.progress(`Uploading ${s} snapshot${s !== 1 ? 's' : ''}...`, !!s);
|
|
267
|
-
});
|
|
268
|
-
} // if dry-running, print the total number of snapshots that would be uploaded
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (this.dryRun && _classPrivateFieldGet(this, _uploads).size) {
|
|
272
|
-
let total = _classPrivateFieldGet(this, _uploads).size - 1; // subtract the build task
|
|
273
|
-
|
|
274
|
-
this.log.info(`Found ${total} snapshot${total !== 1 ? 's' : ''}`);
|
|
275
|
-
} // close the any running server and browser
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
await ((_this$server3 = this.server) === null || _this$server3 === void 0 ? void 0 : _this$server3.close());
|
|
279
|
-
await this.browser.close();
|
|
280
|
-
|
|
281
|
-
if ((_this$build = this.build) !== null && _this$build !== void 0 && _this$build.failed) {
|
|
282
|
-
// do not finalize failed builds
|
|
283
|
-
this.log.warn(`Build #${this.build.number} failed: ${this.build.url}`, meta);
|
|
284
|
-
} else if (this.build) {
|
|
285
|
-
// finalize the build
|
|
286
|
-
await this.client.finalizeBuild(this.build.id);
|
|
287
|
-
this.log.info(`Finalized build #${this.build.number}: ${this.build.url}`, meta);
|
|
288
|
-
} else {
|
|
289
|
-
// no build was ever created (likely failed while deferred)
|
|
290
|
-
this.log.warn('Build not created', meta);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
this.readyState = 3;
|
|
294
|
-
} // Deprecated capture method
|
|
295
|
-
|
|
296
|
-
|
|
342
|
+
// Deprecated capture method
|
|
297
343
|
capture(options) {
|
|
298
344
|
this.log.deprecated('The #capture() method will be ' + 'removed in 1.0.0. Use #snapshot() instead.');
|
|
299
345
|
return this.snapshot(options);
|
|
@@ -328,10 +374,12 @@ class Percy {
|
|
|
328
374
|
yield* (0, _snapshot.discoverSnapshotResources)(this, snapshot, (snap, resources) => {
|
|
329
375
|
if (!this.dryRun) this.log.info(`Snapshot taken: ${snap.name}`, snap.meta);
|
|
330
376
|
|
|
331
|
-
this._scheduleUpload(snap,
|
|
377
|
+
this._scheduleUpload(snap.name, { ...snap,
|
|
378
|
+
resources
|
|
379
|
+
});
|
|
332
380
|
});
|
|
333
381
|
} catch (error) {
|
|
334
|
-
if (error.
|
|
382
|
+
if (error.canceled) {
|
|
335
383
|
this.log.error('Received a duplicate snapshot name, ' + `the previous snapshot was canceled: ${snapshot.name}`);
|
|
336
384
|
} else {
|
|
337
385
|
this.log.error(`Encountered an error taking snapshot: ${snapshot.name}`, snapshot.meta);
|
|
@@ -343,15 +391,15 @@ class Percy {
|
|
|
343
391
|
|
|
344
392
|
return; // eslint-disable-line no-useless-return
|
|
345
393
|
}.bind(this));
|
|
346
|
-
} // Queues a snapshot upload with the provided
|
|
394
|
+
} // Queues a snapshot upload with the provided options
|
|
347
395
|
|
|
348
396
|
|
|
349
|
-
_scheduleUpload(
|
|
350
|
-
_classPrivateFieldGet(this, _uploads).push(`upload/${
|
|
397
|
+
_scheduleUpload(name, options) {
|
|
398
|
+
return _classPrivateFieldGet(this, _uploads).push(`upload/${name}`, async () => {
|
|
351
399
|
try {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
400
|
+
/* istanbul ignore if: useful for other internal packages */
|
|
401
|
+
if (typeof options === 'function') options = await options();
|
|
402
|
+
await this.client.sendSnapshot(this.build.id, options);
|
|
355
403
|
} catch (error) {
|
|
356
404
|
var _error$response, _failed$detail;
|
|
357
405
|
|
|
@@ -360,8 +408,8 @@ class Percy {
|
|
|
360
408
|
|
|
361
409
|
return ((_e$source = e.source) === null || _e$source === void 0 ? void 0 : _e$source.pointer) === '/data/attributes/build';
|
|
362
410
|
});
|
|
363
|
-
this.log.error(`Encountered an error uploading snapshot: ${
|
|
364
|
-
this.log.error((_failed$detail = failed === null || failed === void 0 ? void 0 : failed.detail) !== null && _failed$detail !== void 0 ? _failed$detail : error,
|
|
411
|
+
this.log.error(`Encountered an error uploading snapshot: ${name}`, options.meta);
|
|
412
|
+
this.log.error((_failed$detail = failed === null || failed === void 0 ? void 0 : failed.detail) !== null && _failed$detail !== void 0 ? _failed$detail : error, options.meta); // build failed at some point, stop accepting snapshots
|
|
365
413
|
|
|
366
414
|
if (failed) {
|
|
367
415
|
this.build.failed = true;
|
|
@@ -373,4 +421,6 @@ class Percy {
|
|
|
373
421
|
|
|
374
422
|
}
|
|
375
423
|
|
|
376
|
-
exports.
|
|
424
|
+
exports.Percy = Percy;
|
|
425
|
+
var _default = Percy;
|
|
426
|
+
exports.default = _default;
|
package/dist/queue.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.default = exports.Queue = void 0;
|
|
7
7
|
|
|
8
8
|
var _utils = require("./utils");
|
|
9
9
|
|
|
@@ -19,19 +19,6 @@ function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!priva
|
|
|
19
19
|
|
|
20
20
|
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
|
|
21
21
|
|
|
22
|
-
function isGenerator(obj) {
|
|
23
|
-
return typeof obj.next === 'function' && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function runGeneratorTask(task, arg) {
|
|
27
|
-
if (task.canceled) await task.generator.throw(new Error('Canceled'));
|
|
28
|
-
let {
|
|
29
|
-
done,
|
|
30
|
-
value
|
|
31
|
-
} = await task.generator.next(arg);
|
|
32
|
-
return done ? value : runGeneratorTask(task, value);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
22
|
var _queued = /*#__PURE__*/new WeakMap();
|
|
36
23
|
|
|
37
24
|
var _pending = /*#__PURE__*/new WeakMap();
|
|
@@ -56,7 +43,10 @@ class Queue {
|
|
|
56
43
|
}
|
|
57
44
|
|
|
58
45
|
push(id, callback, priority) {
|
|
59
|
-
if (this.closed
|
|
46
|
+
if (this.closed && !id.startsWith('@@/')) {
|
|
47
|
+
throw new Error('Closed');
|
|
48
|
+
}
|
|
49
|
+
|
|
60
50
|
this.cancel(id);
|
|
61
51
|
let task = {
|
|
62
52
|
id,
|
|
@@ -77,15 +67,19 @@ class Queue {
|
|
|
77
67
|
}
|
|
78
68
|
|
|
79
69
|
cancel(id) {
|
|
80
|
-
|
|
70
|
+
var _classPrivateFieldGet2, _classPrivateFieldGet3;
|
|
81
71
|
|
|
82
|
-
|
|
72
|
+
(_classPrivateFieldGet2 = _classPrivateFieldGet(this, _pending).get(id)) === null || _classPrivateFieldGet2 === void 0 ? void 0 : (_classPrivateFieldGet3 = _classPrivateFieldGet2.cancel) === null || _classPrivateFieldGet3 === void 0 ? void 0 : _classPrivateFieldGet3.call(_classPrivateFieldGet2);
|
|
83
73
|
|
|
84
74
|
_classPrivateFieldGet(this, _pending).delete(id);
|
|
85
75
|
|
|
86
76
|
_classPrivateFieldGet(this, _queued).delete(id);
|
|
87
77
|
}
|
|
88
78
|
|
|
79
|
+
has(id) {
|
|
80
|
+
return _classPrivateFieldGet(this, _queued).has(id) || _classPrivateFieldGet(this, _pending).has(id);
|
|
81
|
+
}
|
|
82
|
+
|
|
89
83
|
clear() {
|
|
90
84
|
_classPrivateFieldGet(this, _queued).clear();
|
|
91
85
|
|
|
@@ -109,32 +103,48 @@ class Queue {
|
|
|
109
103
|
return this;
|
|
110
104
|
}
|
|
111
105
|
|
|
106
|
+
open() {
|
|
107
|
+
this.closed = false;
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
|
|
112
111
|
close(abort) {
|
|
113
112
|
if (abort) this.stop().clear();
|
|
114
113
|
this.closed = true;
|
|
115
114
|
return this;
|
|
116
115
|
}
|
|
117
116
|
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
idle(callback) {
|
|
118
|
+
return (0, _utils.waitFor)(() => {
|
|
119
|
+
callback === null || callback === void 0 ? void 0 : callback(_classPrivateFieldGet(this, _pending).size);
|
|
120
120
|
return !_classPrivateFieldGet(this, _pending).size;
|
|
121
121
|
}, {
|
|
122
122
|
idle: 10
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
empty(callback) {
|
|
127
|
+
return (0, _utils.waitFor)(() => {
|
|
128
|
+
callback === null || callback === void 0 ? void 0 : callback(this.size);
|
|
129
129
|
return !this.size;
|
|
130
130
|
}, {
|
|
131
131
|
idle: 10
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
flush(callback) {
|
|
136
|
+
let stopped = !this.running;
|
|
137
|
+
this.run().push('@@/flush', () => {
|
|
138
|
+
if (stopped) this.stop();
|
|
139
|
+
});
|
|
140
|
+
return this.idle(pend => {
|
|
141
|
+
let left = [..._classPrivateFieldGet(this, _queued).keys()].indexOf('@@/flush');
|
|
142
|
+
if (!~left && !_classPrivateFieldGet(this, _pending).has('@@/flush')) left = 0;
|
|
143
|
+
callback === null || callback === void 0 ? void 0 : callback(pend + left);
|
|
144
|
+
}).canceled(() => {
|
|
145
|
+
if (stopped) this.stop();
|
|
146
|
+
this.cancel('@@/flush');
|
|
147
|
+
});
|
|
138
148
|
}
|
|
139
149
|
|
|
140
150
|
next() {
|
|
@@ -158,27 +168,29 @@ class Queue {
|
|
|
158
168
|
|
|
159
169
|
_classPrivateFieldGet(this, _pending).set(task.id, task);
|
|
160
170
|
|
|
161
|
-
let done =
|
|
162
|
-
|
|
171
|
+
let done = callback => arg => {
|
|
172
|
+
var _task$cancel;
|
|
173
|
+
|
|
174
|
+
if (!((_task$cancel = task.cancel) !== null && _task$cancel !== void 0 && _task$cancel.triggered)) {
|
|
175
|
+
_classPrivateFieldGet(this, _pending).delete(task.id);
|
|
176
|
+
}
|
|
177
|
+
|
|
163
178
|
callback(arg);
|
|
164
179
|
|
|
165
180
|
this._dequeue();
|
|
166
181
|
};
|
|
167
182
|
|
|
168
183
|
try {
|
|
169
|
-
let
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
task.generator = result;
|
|
173
|
-
result = runGeneratorTask(task);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return Promise.resolve(result).then(done.bind(null, task.resolve)).catch(done.bind(null, task.reject));
|
|
184
|
+
let gen = (0, _utils.generatePromise)(task.callback);
|
|
185
|
+
task.cancel = gen.cancel;
|
|
186
|
+
return gen.then(done(task.resolve), done(task.reject));
|
|
177
187
|
} catch (err) {
|
|
178
|
-
done(task.reject
|
|
188
|
+
done(task.reject)(err);
|
|
179
189
|
}
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
}
|
|
183
193
|
|
|
184
|
-
exports.
|
|
194
|
+
exports.Queue = Queue;
|
|
195
|
+
var _default = Queue;
|
|
196
|
+
exports.default = _default;
|
package/dist/server.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.createPercyServer = createPercyServer;
|
|
6
7
|
exports.createServer = createServer;
|
|
7
|
-
exports.default =
|
|
8
|
+
exports.default = void 0;
|
|
8
9
|
|
|
9
10
|
var _fs = _interopRequireDefault(require("fs"));
|
|
10
11
|
|
|
@@ -155,9 +156,12 @@ function createPercyServer(percy) {
|
|
|
155
156
|
}];
|
|
156
157
|
},
|
|
157
158
|
// stops the instance async at the end of the event loop
|
|
158
|
-
'/percy/stop': () =>
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
'/percy/stop': () => {
|
|
160
|
+
setImmediate(async () => await percy.stop());
|
|
161
|
+
return [200, 'application/json', {
|
|
162
|
+
success: true
|
|
163
|
+
}];
|
|
164
|
+
},
|
|
161
165
|
// other routes 404
|
|
162
166
|
default: () => [404, 'application/json', {
|
|
163
167
|
error: 'Not found',
|
|
@@ -185,4 +189,7 @@ function createPercyServer(percy) {
|
|
|
185
189
|
});
|
|
186
190
|
});
|
|
187
191
|
return context;
|
|
188
|
-
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
var _default = createPercyServer;
|
|
195
|
+
exports.default = _default;
|
package/dist/session.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.default = exports.Session = void 0;
|
|
7
7
|
|
|
8
8
|
var _events = _interopRequireDefault(require("events"));
|
|
9
9
|
|
|
@@ -135,4 +135,6 @@ class Session extends _events.default {
|
|
|
135
135
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
exports.
|
|
138
|
+
exports.Session = Session;
|
|
139
|
+
var _default = Session;
|
|
140
|
+
exports.default = _default;
|
package/dist/snapshot.js
CHANGED
|
@@ -247,20 +247,25 @@ async function* discoverSnapshotResources(percy, snapshot, callback) {
|
|
|
247
247
|
yield waitForDiscoveryNetworkIdle(page, snapshot.discovery);
|
|
248
248
|
handleSnapshotResources(snapshot, resources, callback);
|
|
249
249
|
} else {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
let options = { ...snapshot,
|
|
254
|
-
...snap
|
|
255
|
-
}; // will wait for timeouts, selectors, and additional network activity
|
|
250
|
+
let {
|
|
251
|
+
enableJavaScript
|
|
252
|
+
} = snapshot; // capture snapshots sequentially
|
|
256
253
|
|
|
254
|
+
for (let snap of allSnapshots) {
|
|
255
|
+
// will wait for timeouts, selectors, and additional network activity
|
|
257
256
|
let {
|
|
258
257
|
url,
|
|
259
258
|
dom
|
|
260
|
-
} = yield page.snapshot(
|
|
259
|
+
} = yield page.snapshot({
|
|
260
|
+
enableJavaScript,
|
|
261
|
+
...snap
|
|
262
|
+
});
|
|
263
|
+
resources.set(url, (0, _utils2.createRootResource)(url, dom)); // shallow merge with root snapshot options
|
|
264
|
+
|
|
265
|
+
handleSnapshotResources({ ...snapshot,
|
|
266
|
+
...snap
|
|
267
|
+
}, resources, callback); // remove the previously captured dom snapshot
|
|
261
268
|
|
|
262
|
-
resources.set(url, (0, _utils2.createRootResource)(url, dom));
|
|
263
|
-
handleSnapshotResources(options, resources, callback);
|
|
264
269
|
resources.delete(url);
|
|
265
270
|
}
|
|
266
271
|
} // page clean up
|
package/dist/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.createLogResource = createLogResource;
|
|
|
7
7
|
exports.createPercyCSSResource = createPercyCSSResource;
|
|
8
8
|
exports.createResource = createResource;
|
|
9
9
|
exports.createRootResource = createRootResource;
|
|
10
|
+
exports.generatePromise = generatePromise;
|
|
10
11
|
exports.hostname = hostname;
|
|
11
12
|
Object.defineProperty(exports, "hostnameMatches", {
|
|
12
13
|
enumerable: true,
|
|
@@ -15,10 +16,20 @@ Object.defineProperty(exports, "hostnameMatches", {
|
|
|
15
16
|
}
|
|
16
17
|
});
|
|
17
18
|
exports.normalizeURL = normalizeURL;
|
|
19
|
+
Object.defineProperty(exports, "request", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: function () {
|
|
22
|
+
return _request.request;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
18
25
|
exports.waitFor = waitFor;
|
|
19
26
|
|
|
20
27
|
var _utils = require("@percy/client/dist/utils");
|
|
21
28
|
|
|
29
|
+
var _request = require("@percy/client/dist/request");
|
|
30
|
+
|
|
31
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
32
|
+
|
|
22
33
|
// Returns the hostname portion of a URL.
|
|
23
34
|
function hostname(url) {
|
|
24
35
|
return new URL(url).hostname;
|
|
@@ -68,39 +79,92 @@ function createPercyCSSResource(url, css) {
|
|
|
68
79
|
|
|
69
80
|
function createLogResource(logs) {
|
|
70
81
|
return createResource(`/percy.${Date.now()}.log`, JSON.stringify(logs), 'text/plain');
|
|
71
|
-
} //
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
} // Creates a thennable, cancelable, generator instance
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
function generatePromise(gen) {
|
|
86
|
+
var _gen, _gen2;
|
|
87
|
+
|
|
88
|
+
// ensure a generator is provided
|
|
89
|
+
if (typeof gen === 'function') gen = gen();
|
|
90
|
+
if (typeof ((_gen = gen) === null || _gen === void 0 ? void 0 : _gen.then) === 'function') return gen;
|
|
91
|
+
if (typeof ((_gen2 = gen) === null || _gen2 === void 0 ? void 0 : _gen2.next) !== 'function' || !(typeof gen[Symbol.iterator] === 'function' || typeof gen[Symbol.asyncIterator] === 'function')) return Promise.resolve(gen); // used to trigger cancelation
|
|
92
|
+
|
|
93
|
+
class Canceled extends Error {
|
|
94
|
+
constructor(...args) {
|
|
95
|
+
super(...args);
|
|
96
|
+
|
|
97
|
+
_defineProperty(this, "name", 'Canceled');
|
|
75
98
|
|
|
76
|
-
|
|
99
|
+
_defineProperty(this, "canceled", true);
|
|
100
|
+
}
|
|
77
101
|
|
|
102
|
+
} // recursively runs the generator, maybe throwing an error when canceled
|
|
78
103
|
|
|
79
|
-
|
|
104
|
+
|
|
105
|
+
let handleNext = async (g, last) => {
|
|
106
|
+
let canceled = g.cancel.triggered;
|
|
107
|
+
let {
|
|
108
|
+
done,
|
|
109
|
+
value
|
|
110
|
+
} = canceled ? await g.throw(canceled) : await g.next(last);
|
|
111
|
+
if (canceled) delete g.cancel.triggered;
|
|
112
|
+
return done ? value : handleNext(g, value);
|
|
113
|
+
}; // handle cancelation errors by calling any cancel handlers
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
let cancelable = async function* () {
|
|
117
|
+
try {
|
|
118
|
+
return yield* gen;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (error.canceled) {
|
|
121
|
+
let cancelers = cancelable.cancelers || [];
|
|
122
|
+
|
|
123
|
+
for (let c of cancelers) await c(error);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}(); // augment the cancelable generator with promise-like and cancel methods
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
return Object.assign(cancelable, {
|
|
132
|
+
run: () => cancelable.promise || (cancelable.promise = handleNext(cancelable)),
|
|
133
|
+
then: (resolve, reject) => cancelable.run().then(resolve, reject),
|
|
134
|
+
catch: reject => cancelable.run().catch(reject),
|
|
135
|
+
cancel: message => {
|
|
136
|
+
cancelable.cancel.triggered = new Canceled(message);
|
|
137
|
+
return cancelable;
|
|
138
|
+
},
|
|
139
|
+
canceled: handler => {
|
|
140
|
+
(cancelable.cancelers || (cancelable.cancelers = [])).push(handler);
|
|
141
|
+
return cancelable;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
} // Resolves when the predicate function returns true within the timeout. If an idle option is
|
|
145
|
+
// provided, the predicate will be checked again before resolving, after the idle period. The poll
|
|
146
|
+
// option determines how often the predicate check will be run.
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
function waitFor(predicate, options) {
|
|
80
150
|
let {
|
|
81
151
|
poll = 10,
|
|
82
152
|
timeout,
|
|
83
153
|
idle
|
|
84
|
-
} = Number.isInteger(
|
|
85
|
-
timeout:
|
|
86
|
-
} :
|
|
87
|
-
return
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
setTimeout(check, poll, start);
|
|
100
|
-
}
|
|
101
|
-
} catch (error) {
|
|
102
|
-
reject(error);
|
|
154
|
+
} = Number.isInteger(options) ? {
|
|
155
|
+
timeout: options
|
|
156
|
+
} : options || {};
|
|
157
|
+
return generatePromise(async function* check(start, done) {
|
|
158
|
+
while (true) {
|
|
159
|
+
if (timeout && Date.now() - start >= timeout) {
|
|
160
|
+
throw new Error(`Timeout of ${timeout}ms exceeded.`);
|
|
161
|
+
} else if (!predicate()) {
|
|
162
|
+
yield new Promise(r => setTimeout(r, poll, done = false));
|
|
163
|
+
} else if (idle && !done) {
|
|
164
|
+
yield new Promise(r => setTimeout(r, idle, done = true));
|
|
165
|
+
} else {
|
|
166
|
+
return;
|
|
103
167
|
}
|
|
104
|
-
}
|
|
105
|
-
});
|
|
168
|
+
}
|
|
169
|
+
}(Date.now()));
|
|
106
170
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.75",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/percy/cli",
|
|
8
|
+
"directory": "packages/core"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
5
13
|
"main": "dist/index.js",
|
|
6
14
|
"types": "types/index.d.ts",
|
|
7
15
|
"files": [
|
|
@@ -21,23 +29,15 @@
|
|
|
21
29
|
"test:coverage": "yarn test --coverage",
|
|
22
30
|
"test:types": "tsd"
|
|
23
31
|
},
|
|
24
|
-
"publishConfig": {
|
|
25
|
-
"access": "public"
|
|
26
|
-
},
|
|
27
32
|
"dependencies": {
|
|
28
|
-
"@percy/client": "1.0.0-beta.
|
|
29
|
-
"@percy/config": "1.0.0-beta.
|
|
30
|
-
"@percy/dom": "1.0.0-beta.
|
|
31
|
-
"@percy/logger": "1.0.0-beta.
|
|
33
|
+
"@percy/client": "1.0.0-beta.75",
|
|
34
|
+
"@percy/config": "1.0.0-beta.75",
|
|
35
|
+
"@percy/dom": "1.0.0-beta.75",
|
|
36
|
+
"@percy/logger": "1.0.0-beta.75",
|
|
32
37
|
"cross-spawn": "^7.0.3",
|
|
33
38
|
"extract-zip": "^2.0.1",
|
|
34
39
|
"rimraf": "^3.0.2",
|
|
35
40
|
"ws": "^8.0.0"
|
|
36
41
|
},
|
|
37
|
-
"
|
|
38
|
-
"type": "git",
|
|
39
|
-
"url": "https://github.com/percy/cli",
|
|
40
|
-
"directory": "packages/core"
|
|
41
|
-
},
|
|
42
|
-
"gitHead": "364d1df717fb19a26ccb024458df6e78a9c11f99"
|
|
42
|
+
"gitHead": "3b778a9c0b72dcbf113910227f1a073a2714042a"
|
|
43
43
|
}
|
package/test/helpers/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// aliased to src for coverage during tests without needing to compile this file
|
|
2
2
|
const { createServer } = require('@percy/core/dist/server');
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
function createTestServer(routes, port = 8000) {
|
|
5
5
|
let context = createServer(routes);
|
|
6
6
|
|
|
7
7
|
// handle route errors
|
|
@@ -16,3 +16,7 @@ module.exports = function createTestServer(routes, port = 8000) {
|
|
|
16
16
|
// automatically listen
|
|
17
17
|
return context.listen(port);
|
|
18
18
|
};
|
|
19
|
+
|
|
20
|
+
// support commonjs environments
|
|
21
|
+
module.exports = createTestServer;
|
|
22
|
+
module.exports.createTestServer = createTestServer;
|