@percy/core 1.0.0-beta.68 → 1.0.0-beta.71
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 +14 -5
- package/dist/config.js +4 -73
- package/dist/discovery.js +14 -14
- package/dist/install.js +4 -6
- package/dist/network.js +9 -5
- package/dist/page.js +2 -1
- package/dist/percy.js +88 -193
- package/dist/queue.js +48 -18
- package/dist/server.js +18 -5
- package/dist/session.js +5 -1
- package/dist/snapshot.js +274 -0
- package/dist/utils.js +15 -27
- package/package.json +6 -6
- package/types/index.d.ts +15 -7
package/dist/browser.js
CHANGED
|
@@ -29,6 +29,10 @@ var _page = _interopRequireDefault(require("./page"));
|
|
|
29
29
|
|
|
30
30
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
31
31
|
|
|
32
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
33
|
+
|
|
34
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
35
|
+
|
|
32
36
|
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; }
|
|
33
37
|
|
|
34
38
|
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
|
|
@@ -61,12 +65,12 @@ class Browser extends _events.default {
|
|
|
61
65
|
|
|
62
66
|
_defineProperty(this, "closed", false);
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
_classPrivateFieldInitSpec(this, _callbacks, {
|
|
65
69
|
writable: true,
|
|
66
70
|
value: new Map()
|
|
67
71
|
});
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
_classPrivateFieldInitSpec(this, _lastid, {
|
|
70
74
|
writable: true,
|
|
71
75
|
value: 0
|
|
72
76
|
});
|
|
@@ -124,7 +128,8 @@ class Browser extends _events.default {
|
|
|
124
128
|
|
|
125
129
|
this.profile = await _fs.promises.mkdtemp(_path.default.join(_os.default.tmpdir(), 'percy-browser-')); // collect args to pass to the browser process
|
|
126
130
|
|
|
127
|
-
let args = [...this.args, `--user-data-dir=${this.profile}`];
|
|
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
|
|
128
133
|
|
|
129
134
|
this.process = (0, _crossSpawn.default)(this.executable, args, {
|
|
130
135
|
detached: process.platform !== 'win32'
|
|
@@ -139,6 +144,7 @@ class Browser extends _events.default {
|
|
|
139
144
|
this.ws.on('message', data => this._handleMessage(data)); // get version information
|
|
140
145
|
|
|
141
146
|
this.version = await this.send('Browser.getVersion');
|
|
147
|
+
this.log.debug(`Browser connected: ${this.version.product}`);
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
isConnected() {
|
|
@@ -150,7 +156,8 @@ class Browser extends _events.default {
|
|
|
150
156
|
async close() {
|
|
151
157
|
var _this$process4, _this$ws2;
|
|
152
158
|
|
|
153
|
-
if (this._closed) return this._closed;
|
|
159
|
+
if (this._closed) return this._closed;
|
|
160
|
+
this.log.debug('Closing browser'); // resolves when the browser has closed
|
|
154
161
|
|
|
155
162
|
this._closed = Promise.all([new Promise(resolve => {
|
|
156
163
|
/* istanbul ignore next: race condition paranoia */
|
|
@@ -177,6 +184,8 @@ class Browser extends _events.default {
|
|
|
177
184
|
this.log.debug(error);
|
|
178
185
|
});
|
|
179
186
|
}
|
|
187
|
+
|
|
188
|
+
this.log.debug('Browser closed');
|
|
180
189
|
}); // reject any pending callbacks
|
|
181
190
|
|
|
182
191
|
for (let callback of _classPrivateFieldGet(this, _callbacks).values()) {
|
|
@@ -316,7 +325,7 @@ class Browser extends _events.default {
|
|
|
316
325
|
} else if (data.method === 'Target.detachedFromTarget') {
|
|
317
326
|
// remove the old session reference when detached from a target
|
|
318
327
|
let session = this.sessions.get(data.params.sessionId);
|
|
319
|
-
this.sessions.delete(
|
|
328
|
+
this.sessions.delete(data.params.sessionId);
|
|
320
329
|
session === null || session === void 0 ? void 0 : session._handleClose();
|
|
321
330
|
}
|
|
322
331
|
|
package/dist/config.js
CHANGED
|
@@ -4,18 +4,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.configMigration = configMigration;
|
|
7
|
+
exports.snapshotDOMSchema = exports.schemas = exports.migrations = exports.configSchema = void 0;
|
|
7
8
|
exports.snapshotMigration = snapshotMigration;
|
|
8
|
-
exports.
|
|
9
|
-
exports.migrations = exports.schemas = exports.snapshotDOMSchema = exports.snapshotSchema = exports.configSchema = void 0;
|
|
10
|
-
|
|
11
|
-
var _assert = require("assert");
|
|
12
|
-
|
|
13
|
-
var _config = _interopRequireDefault(require("@percy/config"));
|
|
14
|
-
|
|
15
|
-
var _utils = require("@percy/config/dist/utils");
|
|
16
|
-
|
|
17
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
|
-
|
|
9
|
+
exports.snapshotSchema = void 0;
|
|
19
10
|
// Common config options used in Percy commands
|
|
20
11
|
const configSchema = {
|
|
21
12
|
snapshot: {
|
|
@@ -362,65 +353,5 @@ function snapshotMigration(config, util) {
|
|
|
362
353
|
|
|
363
354
|
const schemas = [configSchema, snapshotSchema, snapshotDOMSchema];
|
|
364
355
|
exports.schemas = schemas;
|
|
365
|
-
const migrations = [['/config', configMigration], ['/snapshot', snapshotMigration]];
|
|
366
|
-
|
|
367
|
-
exports.migrations = migrations;
|
|
368
|
-
|
|
369
|
-
function getSnapshotConfig(options, {
|
|
370
|
-
snapshot,
|
|
371
|
-
discovery
|
|
372
|
-
}, log) {
|
|
373
|
-
var _ref, _config$widths;
|
|
374
|
-
|
|
375
|
-
// prune client and env info from being validated
|
|
376
|
-
let {
|
|
377
|
-
clientInfo,
|
|
378
|
-
environmentInfo,
|
|
379
|
-
...config
|
|
380
|
-
} = _config.default.migrate(options, '/snapshot'); // throw an error when missing required options
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
(0, _assert.strict)(config.url, 'Missing required URL for snapshot');
|
|
384
|
-
(0, _assert.strict)((_ref = (_config$widths = config.widths) !== null && _config$widths !== void 0 ? _config$widths : snapshot.widths) === null || _ref === void 0 ? void 0 : _ref.length, 'Missing required widths for snapshot'); // validate and scrub according to dom snaphot presence
|
|
385
|
-
|
|
386
|
-
let errors = _config.default.validate(config, config.domSnapshot ? '/snapshot/dom' : '/snapshot');
|
|
387
|
-
|
|
388
|
-
if (errors) {
|
|
389
|
-
log.warn('Invalid snapshot options:');
|
|
390
|
-
|
|
391
|
-
for (let e of errors) log.warn(`- ${e.path}: ${e.message}`);
|
|
392
|
-
} // parse the URL to construct defaults
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
let url = new URL(options.url); // inherit options from the config
|
|
396
|
-
|
|
397
|
-
return (0, _utils.merge)([snapshot, {
|
|
398
|
-
// default to the URL /pathname?search#hash
|
|
399
|
-
name: `${url.pathname}${url.search}${url.hash}`,
|
|
400
|
-
// add back client and environment information
|
|
401
|
-
clientInfo,
|
|
402
|
-
environmentInfo,
|
|
403
|
-
// only specific discovery options are used per-snapshot
|
|
404
|
-
discovery: {
|
|
405
|
-
allowedHostnames: [url.hostname, ...discovery.allowedHostnames],
|
|
406
|
-
requestHeaders: discovery.requestHeaders,
|
|
407
|
-
authorization: discovery.authorization,
|
|
408
|
-
disableCache: discovery.disableCache,
|
|
409
|
-
userAgent: discovery.userAgent
|
|
410
|
-
}
|
|
411
|
-
}, config], (path, prev, next) => {
|
|
412
|
-
switch (path.join('.')) {
|
|
413
|
-
case 'widths':
|
|
414
|
-
// override and sort widths
|
|
415
|
-
return [path, next.sort((a, b) => a - b)];
|
|
416
|
-
|
|
417
|
-
case 'percyCSS':
|
|
418
|
-
// concatenate percy css
|
|
419
|
-
return [path, [prev, next].filter(Boolean).join('\n')];
|
|
420
|
-
|
|
421
|
-
case 'execute':
|
|
422
|
-
// shorthand for execute.beforeSnapshot
|
|
423
|
-
return Array.isArray(next) || typeof next !== 'object' ? [path.concat('beforeSnapshot'), next] : [path];
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
|
-
}
|
|
356
|
+
const migrations = [['/config', configMigration], ['/snapshot', snapshotMigration]];
|
|
357
|
+
exports.migrations = migrations;
|
package/dist/discovery.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.createRequestHandler = createRequestHandler;
|
|
7
|
-
exports.createRequestFinishedHandler = createRequestFinishedHandler;
|
|
8
6
|
exports.createRequestFailedHandler = createRequestFailedHandler;
|
|
7
|
+
exports.createRequestFinishedHandler = createRequestFinishedHandler;
|
|
8
|
+
exports.createRequestHandler = createRequestHandler;
|
|
9
9
|
|
|
10
10
|
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
11
11
|
|
|
@@ -34,10 +34,10 @@ function createRequestHandler(network, {
|
|
|
34
34
|
let resource = getResource(url);
|
|
35
35
|
|
|
36
36
|
if (resource !== null && resource !== void 0 && resource.root) {
|
|
37
|
-
log.debug('
|
|
37
|
+
log.debug('- Serving root resource', meta);
|
|
38
38
|
await request.respond(resource);
|
|
39
39
|
} else if (resource && !disableCache) {
|
|
40
|
-
log.debug('
|
|
40
|
+
log.debug('- Resource cache hit', meta);
|
|
41
41
|
await request.respond(resource);
|
|
42
42
|
} else {
|
|
43
43
|
await request.continue();
|
|
@@ -57,7 +57,7 @@ function createRequestFinishedHandler(network, {
|
|
|
57
57
|
allowedHostnames,
|
|
58
58
|
disableCache,
|
|
59
59
|
getResource,
|
|
60
|
-
|
|
60
|
+
saveResource
|
|
61
61
|
}) {
|
|
62
62
|
let log = (0, _logger.default)('core:discovery');
|
|
63
63
|
return async request => {
|
|
@@ -80,17 +80,17 @@ function createRequestFinishedHandler(network, {
|
|
|
80
80
|
/* istanbul ignore next: sanity check */
|
|
81
81
|
|
|
82
82
|
if (!response) {
|
|
83
|
-
return log.debug('
|
|
83
|
+
return log.debug('- Skipping no response', meta);
|
|
84
84
|
} else if (!capture) {
|
|
85
|
-
return log.debug('
|
|
85
|
+
return log.debug('- Skipping remote resource', meta);
|
|
86
86
|
} else if (!body.length) {
|
|
87
|
-
return log.debug('
|
|
87
|
+
return log.debug('- Skipping empty response', meta);
|
|
88
88
|
} else if (body.length > MAX_RESOURCE_SIZE) {
|
|
89
|
-
return log.debug('
|
|
89
|
+
return log.debug('- Skipping resource larger than 15MB', meta);
|
|
90
90
|
} else if (!ALLOWED_STATUSES.includes(response.status)) {
|
|
91
|
-
return log.debug(
|
|
91
|
+
return log.debug(`- Skipping disallowed status [${response.status}]`, meta);
|
|
92
92
|
} else if (!enableJavaScript && !ALLOWED_RESOURCES.includes(request.type)) {
|
|
93
|
-
return log.debug(
|
|
93
|
+
return log.debug(`- Skipping disallowed resource type [${request.type}]`, meta);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
resource = (0, _utils.createResource)(url, body, response.mimeType, {
|
|
@@ -101,11 +101,11 @@ function createRequestFinishedHandler(network, {
|
|
|
101
101
|
[key]: value.split('\n')
|
|
102
102
|
}), {})
|
|
103
103
|
});
|
|
104
|
-
log.debug(
|
|
105
|
-
log.debug(
|
|
104
|
+
log.debug(`- sha: ${resource.sha}`, meta);
|
|
105
|
+
log.debug(`- mimetype: ${resource.mimetype}`, meta);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
saveResource(resource);
|
|
109
109
|
} catch (error) {
|
|
110
110
|
log.debug(`Encountered an error processing resource: ${url}`, meta);
|
|
111
111
|
log.debug(error);
|
package/dist/install.js
CHANGED
|
@@ -26,8 +26,8 @@ function formatBytes(int) {
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
function formatTime(ms) {
|
|
29
|
-
let minutes = (ms / 1000 / 60).
|
|
30
|
-
let seconds = (ms / 1000).toFixed().padStart(2, '0');
|
|
29
|
+
let minutes = (ms / 1000 / 60).toString().split('.')[0].padStart(2, '0');
|
|
30
|
+
let seconds = (ms / 1000 % 60).toFixed().padStart(2, '0');
|
|
31
31
|
return `${minutes}:${seconds}`;
|
|
32
32
|
} // Formats progress as ":prefix [:bar] :ratio :percent :eta"
|
|
33
33
|
|
|
@@ -38,9 +38,7 @@ function formatProgress(prefix, total, start, progress) {
|
|
|
38
38
|
let percent = Math.floor(ratio * 100).toFixed(0);
|
|
39
39
|
let barLen = Math.round(width * ratio);
|
|
40
40
|
let barContent = Array(Math.max(0, barLen + 1)).join('=') + Array(Math.max(0, width - barLen + 1)).join(' ');
|
|
41
|
-
let elapsed =
|
|
42
|
-
/* istanbul ignore next: eta in testing is always 0 */
|
|
43
|
-
|
|
41
|
+
let elapsed = Date.now() - start;
|
|
44
42
|
let eta = ratio >= 1 ? 0 : elapsed * (total / progress - 1);
|
|
45
43
|
return `${prefix} [${barContent}] ` + `${formatBytes(progress)}/${formatBytes(total)} ` + `${percent}% ${formatTime(eta)}`;
|
|
46
44
|
} // Returns an item from the map keyed by the current platform
|
|
@@ -144,7 +142,7 @@ async function install({
|
|
|
144
142
|
response.on('data', chunk => {
|
|
145
143
|
var _start, _progress;
|
|
146
144
|
|
|
147
|
-
(_start = start) !== null && _start !== void 0 ? _start : start =
|
|
145
|
+
(_start = start) !== null && _start !== void 0 ? _start : start = Date.now();
|
|
148
146
|
progress = ((_progress = progress) !== null && _progress !== void 0 ? _progress : 0) + chunk.length;
|
|
149
147
|
log.progress(formatProgress(premsg, total, start, progress));
|
|
150
148
|
});
|
package/dist/network.js
CHANGED
|
@@ -13,6 +13,10 @@ var _discovery = require("./discovery");
|
|
|
13
13
|
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
15
|
|
|
16
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
17
|
+
|
|
18
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
19
|
+
|
|
16
20
|
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; }
|
|
17
21
|
|
|
18
22
|
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
|
|
@@ -37,22 +41,22 @@ class Network {
|
|
|
37
41
|
|
|
38
42
|
_defineProperty(this, "log", (0, _logger.default)('core:network'));
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
_classPrivateFieldInitSpec(this, _pending, {
|
|
41
45
|
writable: true,
|
|
42
46
|
value: new Map()
|
|
43
47
|
});
|
|
44
48
|
|
|
45
|
-
|
|
49
|
+
_classPrivateFieldInitSpec(this, _requests, {
|
|
46
50
|
writable: true,
|
|
47
51
|
value: new Map()
|
|
48
52
|
});
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
_classPrivateFieldInitSpec(this, _intercepts, {
|
|
51
55
|
writable: true,
|
|
52
56
|
value: new Map()
|
|
53
57
|
});
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
_classPrivateFieldInitSpec(this, _authentications, {
|
|
56
60
|
writable: true,
|
|
57
61
|
value: new Set()
|
|
58
62
|
});
|
|
@@ -323,7 +327,7 @@ class Network {
|
|
|
323
327
|
let msg = 'Timed out waiting for network requests to idle.';
|
|
324
328
|
|
|
325
329
|
if (this.log.shouldLog('debug')) {
|
|
326
|
-
msg += `\n\n ${['Active requests:', ...requests.map(r => r.url)].join('\n
|
|
330
|
+
msg += `\n\n ${['Active requests:', ...requests.map(r => r.url)].join('\n - ')}\n`;
|
|
327
331
|
}
|
|
328
332
|
|
|
329
333
|
throw new Error(msg);
|
package/dist/page.js
CHANGED
|
@@ -230,7 +230,8 @@ class Page {
|
|
|
230
230
|
execute,
|
|
231
231
|
...options
|
|
232
232
|
}) {
|
|
233
|
-
// wait for any specified timeout
|
|
233
|
+
this.log.debug(`Taking snapshot: ${name}`, this.meta); // wait for any specified timeout
|
|
234
|
+
|
|
234
235
|
if (waitForTimeout) {
|
|
235
236
|
this.log.debug(`Wait for ${waitForTimeout}ms timeout`, this.meta);
|
|
236
237
|
await new Promise(resolve => setTimeout(resolve, waitForTimeout));
|
package/dist/percy.js
CHANGED
|
@@ -9,6 +9,8 @@ var _client = _interopRequireDefault(require("@percy/client"));
|
|
|
9
9
|
|
|
10
10
|
var _config = _interopRequireDefault(require("@percy/config"));
|
|
11
11
|
|
|
12
|
+
var _utils = require("@percy/config/dist/utils");
|
|
13
|
+
|
|
12
14
|
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
13
15
|
|
|
14
16
|
var _queue = _interopRequireDefault(require("./queue"));
|
|
@@ -17,12 +19,14 @@ var _browser = _interopRequireDefault(require("./browser"));
|
|
|
17
19
|
|
|
18
20
|
var _server = _interopRequireDefault(require("./server"));
|
|
19
21
|
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
var _utils = require("./utils");
|
|
22
|
+
var _snapshot = require("./snapshot");
|
|
23
23
|
|
|
24
24
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
25
|
|
|
26
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
27
|
+
|
|
28
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
29
|
+
|
|
26
30
|
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; }
|
|
27
31
|
|
|
28
32
|
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
|
|
@@ -31,8 +35,6 @@ function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!priva
|
|
|
31
35
|
|
|
32
36
|
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
|
|
33
37
|
|
|
34
|
-
var _cache = /*#__PURE__*/new WeakMap();
|
|
35
|
-
|
|
36
38
|
var _uploads = /*#__PURE__*/new WeakMap();
|
|
37
39
|
|
|
38
40
|
var _snapshots = /*#__PURE__*/new WeakMap();
|
|
@@ -56,6 +58,8 @@ class Percy {
|
|
|
56
58
|
deferUploads,
|
|
57
59
|
// run without uploading anything
|
|
58
60
|
skipUploads,
|
|
61
|
+
// implies `skipUploads` and also skips asset discovery
|
|
62
|
+
dryRun,
|
|
59
63
|
// configuration filepath
|
|
60
64
|
config,
|
|
61
65
|
// provided to @percy/client
|
|
@@ -73,24 +77,20 @@ class Percy {
|
|
|
73
77
|
|
|
74
78
|
_defineProperty(this, "readyState", null);
|
|
75
79
|
|
|
76
|
-
|
|
77
|
-
writable: true,
|
|
78
|
-
value: new Map()
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
_uploads.set(this, {
|
|
80
|
+
_classPrivateFieldInitSpec(this, _uploads, {
|
|
82
81
|
writable: true,
|
|
83
82
|
value: new _queue.default()
|
|
84
83
|
});
|
|
85
84
|
|
|
86
|
-
|
|
85
|
+
_classPrivateFieldInitSpec(this, _snapshots, {
|
|
87
86
|
writable: true,
|
|
88
87
|
value: new _queue.default()
|
|
89
88
|
});
|
|
90
89
|
|
|
91
90
|
if (loglevel) this.loglevel(loglevel);
|
|
92
|
-
this.
|
|
93
|
-
this.skipUploads = skipUploads;
|
|
91
|
+
this.dryRun = !!dryRun;
|
|
92
|
+
this.skipUploads = this.dryRun || !!skipUploads;
|
|
93
|
+
this.deferUploads = this.skipUploads || !!deferUploads;
|
|
94
94
|
this.config = _config.default.load({
|
|
95
95
|
overrides: options,
|
|
96
96
|
path: config
|
|
@@ -123,6 +123,36 @@ class Percy {
|
|
|
123
123
|
|
|
124
124
|
address() {
|
|
125
125
|
return `http://localhost:${this.port}`;
|
|
126
|
+
} // Set client & environment info, and override loaded config options
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
setConfig({
|
|
130
|
+
clientInfo,
|
|
131
|
+
environmentInfo,
|
|
132
|
+
...config
|
|
133
|
+
}) {
|
|
134
|
+
this.client.addClientInfo(clientInfo);
|
|
135
|
+
this.client.addEnvironmentInfo(environmentInfo); // normalize config and do nothing if empty
|
|
136
|
+
|
|
137
|
+
config = _config.default.normalize(config, {
|
|
138
|
+
schema: '/config'
|
|
139
|
+
});
|
|
140
|
+
if (!config) return this.config; // validate provided config options
|
|
141
|
+
|
|
142
|
+
let errors = _config.default.validate(config);
|
|
143
|
+
|
|
144
|
+
if (errors) {
|
|
145
|
+
this.log.warn('Invalid config:');
|
|
146
|
+
|
|
147
|
+
for (let e of errors) this.log.warn(`- ${e.path}: ${e.message}`);
|
|
148
|
+
} // merge and override existing config options
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
this.config = (0, _utils.merge)([this.config, config], (path, prev, next) => {
|
|
152
|
+
// replace arrays instead of merging
|
|
153
|
+
return Array.isArray(next) && [path, next];
|
|
154
|
+
});
|
|
155
|
+
return this.config;
|
|
126
156
|
} // Resolves once snapshot and upload queues are idle
|
|
127
157
|
|
|
128
158
|
|
|
@@ -186,7 +216,7 @@ class Percy {
|
|
|
186
216
|
// when not deferred, wait until the build is created first
|
|
187
217
|
if (!this.deferUploads) await buildTask; // launch the discovery browser
|
|
188
218
|
|
|
189
|
-
await this.browser.launch(
|
|
219
|
+
if (!this.dryRun) await this.browser.launch(); // if there is a server, start listening
|
|
190
220
|
|
|
191
221
|
await ((_this$server = this.server) === null || _this$server === void 0 ? void 0 : _this$server.listen(this.port)); // mark this process as running
|
|
192
222
|
|
|
@@ -226,17 +256,22 @@ class Percy {
|
|
|
226
256
|
};
|
|
227
257
|
if (force) this.log.info('Stopping percy...', meta); // close the snapshot queue and wait for it to empty
|
|
228
258
|
|
|
229
|
-
if (_classPrivateFieldGet(this, _snapshots).close().
|
|
230
|
-
await _classPrivateFieldGet(this, _snapshots).empty(
|
|
231
|
-
this.log.progress(`Processing ${len}` + ` snapshot${len !== 1 ? 's' : ''}...`, !!len);
|
|
232
|
-
});
|
|
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));
|
|
233
261
|
} // run, close, and wait for the upload queue to empty
|
|
234
262
|
|
|
235
263
|
|
|
236
|
-
if (!this.skipUploads && _classPrivateFieldGet(this, _uploads).run().close().
|
|
237
|
-
await _classPrivateFieldGet(this, _uploads).empty(
|
|
238
|
-
this.log.progress(`Uploading ${
|
|
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);
|
|
239
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' : ''}`);
|
|
240
275
|
} // close the any running server and browser
|
|
241
276
|
|
|
242
277
|
|
|
@@ -271,202 +306,62 @@ class Percy {
|
|
|
271
306
|
snapshot(options) {
|
|
272
307
|
if (this.readyState !== 1) {
|
|
273
308
|
throw new Error('Not running');
|
|
274
|
-
}
|
|
309
|
+
} // handle multiple snapshots
|
|
275
310
|
|
|
276
|
-
let {
|
|
277
|
-
url,
|
|
278
|
-
name,
|
|
279
|
-
discovery,
|
|
280
|
-
domSnapshot,
|
|
281
|
-
execute,
|
|
282
|
-
waitForTimeout,
|
|
283
|
-
waitForSelector,
|
|
284
|
-
additionalSnapshots,
|
|
285
|
-
...conf
|
|
286
|
-
} = (0, _config2.getSnapshotConfig)(options, this.config, this.log);
|
|
287
|
-
let meta = {
|
|
288
|
-
snapshot: {
|
|
289
|
-
name
|
|
290
|
-
},
|
|
291
|
-
build: this.build
|
|
292
|
-
};
|
|
293
311
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
312
|
+
if (Array.isArray(options)) {
|
|
313
|
+
return Promise.all(options.map(o => this.snapshot(o)));
|
|
314
|
+
} // get derived snapshot config options
|
|
297
315
|
|
|
298
316
|
|
|
299
|
-
|
|
317
|
+
let snapshot = (0, _snapshot.getSnapshotConfig)(this, options); // clear any existing snapshot uploads of the same name (for retries)
|
|
300
318
|
|
|
319
|
+
for (let {
|
|
320
|
+
name
|
|
321
|
+
} of [snapshot, ...(snapshot.additionalSnapshots || [])]) {
|
|
322
|
+
_classPrivateFieldGet(this, _uploads).cancel(`upload/${name}`);
|
|
323
|
+
} // resolves after asset discovery has finished and uploads have been queued
|
|
301
324
|
|
|
302
|
-
return _classPrivateFieldGet(this, _snapshots).push(`snapshot/${name}`, async () => {
|
|
303
|
-
let resources = new Map();
|
|
304
|
-
let root, page;
|
|
305
325
|
|
|
326
|
+
return _classPrivateFieldGet(this, _snapshots).push(`snapshot/${snapshot.name}`, async function* () {
|
|
306
327
|
try {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
maybeDebug(conf.enableJavaScript, v => `-> enableJavaScript: ${v}`);
|
|
316
|
-
maybeDebug((_options$discovery = options.discovery) === null || _options$discovery === void 0 ? void 0 : _options$discovery.allowedHostnames, v => `-> discovery.allowedHostnames: ${v}`);
|
|
317
|
-
maybeDebug((_options$discovery2 = options.discovery) === null || _options$discovery2 === void 0 ? void 0 : _options$discovery2.requestHeaders, v => `-> discovery.requestHeaders: ${JSON.stringify(v)}`);
|
|
318
|
-
maybeDebug((_options$discovery3 = options.discovery) === null || _options$discovery3 === void 0 ? void 0 : _options$discovery3.authorization, v => `-> discovery.authorization: ${JSON.stringify(v)}`);
|
|
319
|
-
maybeDebug((_options$discovery4 = options.discovery) === null || _options$discovery4 === void 0 ? void 0 : _options$discovery4.disableCache, v => `-> discovery.disableCache: ${v}`);
|
|
320
|
-
maybeDebug((_options$discovery5 = options.discovery) === null || _options$discovery5 === void 0 ? void 0 : _options$discovery5.userAgent, v => `-> discovery.userAgent: ${v}`);
|
|
321
|
-
maybeDebug(options.waitForTimeout, v => `-> waitForTimeout: ${v}`);
|
|
322
|
-
maybeDebug(options.waitForSelector, v => `-> waitForSelector: ${v}`);
|
|
323
|
-
maybeDebug(options.execute, v => `-> execute: ${v}`);
|
|
324
|
-
maybeDebug(conf.clientInfo, v => `-> clientInfo: ${v}`);
|
|
325
|
-
maybeDebug(conf.environmentInfo, v => `-> environmentInfo: ${v}`); // create the root resource if a dom snapshot was provided
|
|
326
|
-
|
|
327
|
-
if (domSnapshot) {
|
|
328
|
-
root = (0, _utils.createRootResource)(url, domSnapshot);
|
|
329
|
-
} // open a new browser page
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
page = await this.browser.page({
|
|
333
|
-
networkIdleTimeout: this.config.discovery.networkIdleTimeout,
|
|
334
|
-
enableJavaScript: (_conf$enableJavaScrip = conf.enableJavaScript) !== null && _conf$enableJavaScrip !== void 0 ? _conf$enableJavaScrip : !domSnapshot,
|
|
335
|
-
requestHeaders: discovery.requestHeaders,
|
|
336
|
-
authorization: discovery.authorization,
|
|
337
|
-
userAgent: discovery.userAgent,
|
|
338
|
-
meta,
|
|
339
|
-
// enable network inteception
|
|
340
|
-
intercept: {
|
|
341
|
-
disableCache: discovery.disableCache,
|
|
342
|
-
enableJavaScript: conf.enableJavaScript,
|
|
343
|
-
allowedHostnames: discovery.allowedHostnames,
|
|
344
|
-
getResource: url => {
|
|
345
|
-
var _root;
|
|
346
|
-
|
|
347
|
-
return url === ((_root = root) === null || _root === void 0 ? void 0 : _root.url) ? root : resources.get(url) || _classPrivateFieldGet(this, _cache).get(url);
|
|
348
|
-
},
|
|
349
|
-
addResource: resource => {
|
|
350
|
-
if (resource.root) return;
|
|
351
|
-
resources.set(resource.url, resource);
|
|
352
|
-
|
|
353
|
-
_classPrivateFieldGet(this, _cache).set(resource.url, resource);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}); // copy widths to prevent mutation
|
|
357
|
-
|
|
358
|
-
let widths = conf.widths.slice(); // resolves when the network is idle for discoverable assets
|
|
359
|
-
|
|
360
|
-
let discoveryIdle = () => page.network.idle(({
|
|
361
|
-
url
|
|
362
|
-
}) => (0, _utils.hostnameMatches)(discovery.allowedHostnames, url)); // set the initial page size
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
await page.resize({
|
|
366
|
-
width: widths.shift(),
|
|
367
|
-
height: conf.minHeight
|
|
368
|
-
}); // navigate to the url
|
|
369
|
-
|
|
370
|
-
await page.goto(url); // evaluate any necessary javascript
|
|
371
|
-
|
|
372
|
-
await page.evaluate(execute === null || execute === void 0 ? void 0 : execute.afterNavigation); // trigger resize events for other widths
|
|
373
|
-
|
|
374
|
-
for (let width of widths) {
|
|
375
|
-
await page.evaluate(execute === null || execute === void 0 ? void 0 : execute.beforeResize);
|
|
376
|
-
await discoveryIdle(); // ensure discovery idles before resizing
|
|
377
|
-
|
|
378
|
-
await page.resize({
|
|
379
|
-
width,
|
|
380
|
-
height: conf.minHeight
|
|
381
|
-
});
|
|
382
|
-
await page.evaluate(execute === null || execute === void 0 ? void 0 : execute.afterResize);
|
|
383
|
-
} // create and add a percy-css resource
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
let percyCSS = (0, _utils.createPercyCSSResource)(url, conf.percyCSS);
|
|
387
|
-
if (percyCSS) resources.set(percyCSS.url, percyCSS);
|
|
388
|
-
|
|
389
|
-
if (root) {
|
|
390
|
-
// ensure discovery finishes before uploading
|
|
391
|
-
await discoveryIdle();
|
|
392
|
-
root = (0, _utils.injectPercyCSS)(root, percyCSS);
|
|
393
|
-
this.log.info(`Snapshot taken: ${name}`, meta);
|
|
394
|
-
|
|
395
|
-
this._scheduleUpload(name, conf, [root, ...resources.values()]);
|
|
328
|
+
yield* (0, _snapshot.discoverSnapshotResources)(this, snapshot, (snap, resources) => {
|
|
329
|
+
if (!this.dryRun) this.log.info(`Snapshot taken: ${snap.name}`, snap.meta);
|
|
330
|
+
|
|
331
|
+
this._scheduleUpload(snap, resources);
|
|
332
|
+
});
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (error.message === 'Canceled') {
|
|
335
|
+
this.log.error('Received a duplicate snapshot name, ' + `the previous snapshot was canceled: ${snapshot.name}`);
|
|
396
336
|
} else {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
name,
|
|
400
|
-
waitForTimeout,
|
|
401
|
-
waitForSelector,
|
|
402
|
-
execute
|
|
403
|
-
};
|
|
404
|
-
let allSnapshots = [rootSnapshot, ...(additionalSnapshots || [])];
|
|
405
|
-
|
|
406
|
-
for (let {
|
|
407
|
-
name,
|
|
408
|
-
prefix = '',
|
|
409
|
-
suffix = '',
|
|
410
|
-
...opts
|
|
411
|
-
} of allSnapshots) {
|
|
412
|
-
name || (name = `${prefix}${rootSnapshot.name}${suffix}`);
|
|
413
|
-
this.log.debug(`Taking snapshot: ${name}`, meta); // will wait for timeouts, selectors, and additional network activity
|
|
414
|
-
|
|
415
|
-
let {
|
|
416
|
-
url,
|
|
417
|
-
dom
|
|
418
|
-
} = await page.snapshot({ ...conf,
|
|
419
|
-
...opts
|
|
420
|
-
});
|
|
421
|
-
let root = (0, _utils.injectPercyCSS)((0, _utils.createRootResource)(url, dom), percyCSS);
|
|
422
|
-
resources.delete(root.url); // remove any discovered root resource
|
|
423
|
-
|
|
424
|
-
this.log.info(`Snapshot taken: ${name}`, meta);
|
|
425
|
-
|
|
426
|
-
this._scheduleUpload(name, conf, [root, ...resources.values()]);
|
|
427
|
-
}
|
|
337
|
+
this.log.error(`Encountered an error taking snapshot: ${snapshot.name}`, snapshot.meta);
|
|
338
|
+
this.log.error(error, snapshot.meta);
|
|
428
339
|
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
this.log.error(error, meta);
|
|
432
|
-
} finally {
|
|
433
|
-
var _page;
|
|
340
|
+
} // fixes an issue in Node 12 where implicit returns do not correctly resolve the async
|
|
341
|
+
// generator objects — https://crbug.com/v8/10238
|
|
434
342
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
});
|
|
343
|
+
|
|
344
|
+
return; // eslint-disable-line no-useless-return
|
|
345
|
+
}.bind(this));
|
|
438
346
|
} // Queues a snapshot upload with the provided configuration options and resources
|
|
439
347
|
|
|
440
348
|
|
|
441
|
-
_scheduleUpload(
|
|
442
|
-
_classPrivateFieldGet(this, _uploads).push(`upload/${name}`, async () => {
|
|
349
|
+
_scheduleUpload(snapshot, resources) {
|
|
350
|
+
_classPrivateFieldGet(this, _uploads).push(`upload/${snapshot.name}`, async () => {
|
|
443
351
|
try {
|
|
444
|
-
|
|
445
|
-
resources = resources.concat((0, _utils.createLogResource)(_logger.default.query(l => {
|
|
446
|
-
var _l$meta$snapshot;
|
|
447
|
-
|
|
448
|
-
return ((_l$meta$snapshot = l.meta.snapshot) === null || _l$meta$snapshot === void 0 ? void 0 : _l$meta$snapshot.name) === name;
|
|
449
|
-
})));
|
|
450
|
-
await this.client.sendSnapshot(this.build.id, { ...conf,
|
|
451
|
-
name,
|
|
352
|
+
await this.client.sendSnapshot(this.build.id, { ...snapshot,
|
|
452
353
|
resources
|
|
453
354
|
});
|
|
454
355
|
} catch (error) {
|
|
455
356
|
var _error$response, _failed$detail;
|
|
456
357
|
|
|
457
|
-
let meta = {
|
|
458
|
-
snapshot: {
|
|
459
|
-
name
|
|
460
|
-
},
|
|
461
|
-
build: this.build
|
|
462
|
-
};
|
|
463
358
|
let failed = ((_error$response = error.response) === null || _error$response === void 0 ? void 0 : _error$response.status) === 422 && error.response.body.errors.find(e => {
|
|
464
359
|
var _e$source;
|
|
465
360
|
|
|
466
361
|
return ((_e$source = e.source) === null || _e$source === void 0 ? void 0 : _e$source.pointer) === '/data/attributes/build';
|
|
467
362
|
});
|
|
468
|
-
this.log.error(`Encountered an error uploading snapshot: ${name}`, meta);
|
|
469
|
-
this.log.error((_failed$detail = failed === null || failed === void 0 ? void 0 : failed.detail) !== null && _failed$detail !== void 0 ? _failed$detail : error, meta); // build failed at some point, stop accepting snapshots
|
|
363
|
+
this.log.error(`Encountered an error uploading snapshot: ${snapshot.name}`, snapshot.meta);
|
|
364
|
+
this.log.error((_failed$detail = failed === null || failed === void 0 ? void 0 : failed.detail) !== null && _failed$detail !== void 0 ? _failed$detail : error, snapshot.meta); // build failed at some point, stop accepting snapshots
|
|
470
365
|
|
|
471
366
|
if (failed) {
|
|
472
367
|
this.build.failed = true;
|
package/dist/queue.js
CHANGED
|
@@ -7,6 +7,10 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _utils = require("./utils");
|
|
9
9
|
|
|
10
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
11
|
+
|
|
12
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
13
|
+
|
|
10
14
|
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; }
|
|
11
15
|
|
|
12
16
|
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
|
|
@@ -15,6 +19,19 @@ function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!priva
|
|
|
15
19
|
|
|
16
20
|
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
|
|
17
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
|
+
|
|
18
35
|
var _queued = /*#__PURE__*/new WeakMap();
|
|
19
36
|
|
|
20
37
|
var _pending = /*#__PURE__*/new WeakMap();
|
|
@@ -25,12 +42,12 @@ class Queue {
|
|
|
25
42
|
|
|
26
43
|
_defineProperty(this, "closed", false);
|
|
27
44
|
|
|
28
|
-
|
|
45
|
+
_classPrivateFieldInitSpec(this, _queued, {
|
|
29
46
|
writable: true,
|
|
30
47
|
value: new Map()
|
|
31
48
|
});
|
|
32
49
|
|
|
33
|
-
|
|
50
|
+
_classPrivateFieldInitSpec(this, _pending, {
|
|
34
51
|
writable: true,
|
|
35
52
|
value: new Map()
|
|
36
53
|
});
|
|
@@ -40,6 +57,7 @@ class Queue {
|
|
|
40
57
|
|
|
41
58
|
push(id, callback, priority) {
|
|
42
59
|
if (this.closed) throw new Error('Closed');
|
|
60
|
+
this.cancel(id);
|
|
43
61
|
let task = {
|
|
44
62
|
id,
|
|
45
63
|
callback,
|
|
@@ -51,8 +69,6 @@ class Queue {
|
|
|
51
69
|
reject
|
|
52
70
|
});
|
|
53
71
|
|
|
54
|
-
_classPrivateFieldGet(this, _queued).delete(id);
|
|
55
|
-
|
|
56
72
|
_classPrivateFieldGet(this, _queued).set(id, task);
|
|
57
73
|
|
|
58
74
|
this._dequeue();
|
|
@@ -60,17 +76,23 @@ class Queue {
|
|
|
60
76
|
return task.promise;
|
|
61
77
|
}
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
_classPrivateFieldGet(this, _queued).clear();
|
|
68
|
-
}
|
|
79
|
+
cancel(id) {
|
|
80
|
+
let pending = _classPrivateFieldGet(this, _pending).get(id);
|
|
81
|
+
|
|
82
|
+
if (pending) pending.canceled = true;
|
|
69
83
|
|
|
70
|
-
|
|
84
|
+
_classPrivateFieldGet(this, _pending).delete(id);
|
|
85
|
+
|
|
86
|
+
_classPrivateFieldGet(this, _queued).delete(id);
|
|
71
87
|
}
|
|
72
88
|
|
|
73
|
-
|
|
89
|
+
clear() {
|
|
90
|
+
_classPrivateFieldGet(this, _queued).clear();
|
|
91
|
+
|
|
92
|
+
return this.size;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get size() {
|
|
74
96
|
return _classPrivateFieldGet(this, _queued).size + _classPrivateFieldGet(this, _pending).size;
|
|
75
97
|
}
|
|
76
98
|
|
|
@@ -94,15 +116,17 @@ class Queue {
|
|
|
94
116
|
}
|
|
95
117
|
|
|
96
118
|
async idle() {
|
|
97
|
-
await (0, _utils.waitFor)(() =>
|
|
119
|
+
await (0, _utils.waitFor)(() => {
|
|
120
|
+
return !_classPrivateFieldGet(this, _pending).size;
|
|
121
|
+
}, {
|
|
98
122
|
idle: 10
|
|
99
123
|
});
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
async empty(onCheck) {
|
|
103
127
|
await (0, _utils.waitFor)(() => {
|
|
104
|
-
onCheck === null || onCheck === void 0 ? void 0 : onCheck(this.
|
|
105
|
-
return !this.
|
|
128
|
+
onCheck === null || onCheck === void 0 ? void 0 : onCheck(this.size);
|
|
129
|
+
return !this.size;
|
|
106
130
|
}, {
|
|
107
131
|
idle: 10
|
|
108
132
|
});
|
|
@@ -135,15 +159,21 @@ class Queue {
|
|
|
135
159
|
_classPrivateFieldGet(this, _pending).set(task.id, task);
|
|
136
160
|
|
|
137
161
|
let done = (callback, arg) => {
|
|
138
|
-
_classPrivateFieldGet(this, _pending).delete(task.id);
|
|
139
|
-
|
|
162
|
+
if (!task.canceled) _classPrivateFieldGet(this, _pending).delete(task.id);
|
|
140
163
|
callback(arg);
|
|
141
164
|
|
|
142
165
|
this._dequeue();
|
|
143
166
|
};
|
|
144
167
|
|
|
145
168
|
try {
|
|
146
|
-
|
|
169
|
+
let result = task.callback();
|
|
170
|
+
|
|
171
|
+
if (isGenerator(result)) {
|
|
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));
|
|
147
177
|
} catch (err) {
|
|
148
178
|
done(task.reject, err);
|
|
149
179
|
}
|
package/dist/server.js
CHANGED
|
@@ -74,6 +74,7 @@ function createServer(routes) {
|
|
|
74
74
|
|
|
75
75
|
context.routes = routes;
|
|
76
76
|
context.server = _http.default.createServer((request, response) => {
|
|
77
|
+
request.params = new URLSearchParams(request.url.split('?')[1]);
|
|
77
78
|
request.on('data', chunk => {
|
|
78
79
|
request.body = (request.body || '') + chunk;
|
|
79
80
|
});
|
|
@@ -123,6 +124,13 @@ function createPercyServer(percy) {
|
|
|
123
124
|
loglevel: percy.loglevel(),
|
|
124
125
|
build: percy.build
|
|
125
126
|
}],
|
|
127
|
+
// remotely get and set percy config options
|
|
128
|
+
'/percy/config': ({
|
|
129
|
+
body
|
|
130
|
+
}) => [200, 'application/json', {
|
|
131
|
+
config: body ? percy.setConfig(body) : percy.config,
|
|
132
|
+
success: true
|
|
133
|
+
}],
|
|
126
134
|
// responds when idle
|
|
127
135
|
'/percy/idle': () => percy.idle().then(() => [200, 'application/json', {
|
|
128
136
|
success: true
|
|
@@ -136,11 +144,16 @@ function createPercyServer(percy) {
|
|
|
136
144
|
return [200, 'applicaton/javascript', content.concat(wrapper)];
|
|
137
145
|
}),
|
|
138
146
|
// forward snapshot requests
|
|
139
|
-
'/percy/snapshot': ({
|
|
140
|
-
body
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
147
|
+
'/percy/snapshot': async ({
|
|
148
|
+
body,
|
|
149
|
+
params
|
|
150
|
+
}) => {
|
|
151
|
+
let snapshot = percy.snapshot(body);
|
|
152
|
+
if (!params.has('async')) await snapshot;
|
|
153
|
+
return [200, 'application/json', {
|
|
154
|
+
success: true
|
|
155
|
+
}];
|
|
156
|
+
},
|
|
144
157
|
// stops the instance async at the end of the event loop
|
|
145
158
|
'/percy/stop': () => setImmediate(() => percy.stop()) && [200, 'application/json', {
|
|
146
159
|
success: true
|
package/dist/session.js
CHANGED
|
@@ -13,6 +13,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
13
13
|
|
|
14
14
|
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; }
|
|
15
15
|
|
|
16
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
17
|
+
|
|
18
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
19
|
+
|
|
16
20
|
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
|
|
17
21
|
|
|
18
22
|
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
|
|
@@ -30,7 +34,7 @@ class Session extends _events.default {
|
|
|
30
34
|
|
|
31
35
|
super();
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
_classPrivateFieldInitSpec(this, _callbacks, {
|
|
34
38
|
writable: true,
|
|
35
39
|
value: new Map()
|
|
36
40
|
});
|
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.discoverSnapshotResources = discoverSnapshotResources;
|
|
7
|
+
exports.getSnapshotConfig = getSnapshotConfig;
|
|
8
|
+
|
|
9
|
+
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
10
|
+
|
|
11
|
+
var _config = _interopRequireDefault(require("@percy/config"));
|
|
12
|
+
|
|
13
|
+
var _utils = require("@percy/config/dist/utils");
|
|
14
|
+
|
|
15
|
+
var _utils2 = require("./utils");
|
|
16
|
+
|
|
17
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
|
+
|
|
19
|
+
// Validates and returns snapshot options merged with percy config options.
|
|
20
|
+
function getSnapshotConfig(percy, options) {
|
|
21
|
+
var _ref, _snapshot$widths;
|
|
22
|
+
|
|
23
|
+
if (!options.url) throw new Error('Missing required URL for snapshot');
|
|
24
|
+
let {
|
|
25
|
+
config
|
|
26
|
+
} = percy;
|
|
27
|
+
let uri = new URL(options.url);
|
|
28
|
+
let name = options.name || `${uri.pathname}${uri.search}${uri.hash}`;
|
|
29
|
+
let meta = {
|
|
30
|
+
snapshot: {
|
|
31
|
+
name
|
|
32
|
+
},
|
|
33
|
+
build: percy.build
|
|
34
|
+
};
|
|
35
|
+
let log = (0, _logger.default)('core:snapshot'); // migrate deprecated snapshot config options
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
clientInfo,
|
|
39
|
+
environmentInfo,
|
|
40
|
+
...snapshot
|
|
41
|
+
} = _config.default.migrate(options, '/snapshot'); // throw an error when missing required widths
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if (!((_ref = (_snapshot$widths = snapshot.widths) !== null && _snapshot$widths !== void 0 ? _snapshot$widths : percy.config.snapshot.widths) !== null && _ref !== void 0 && _ref.length)) {
|
|
45
|
+
throw new Error('Missing required widths for snapshot');
|
|
46
|
+
} // validate and scrub according to dom snaphot presence
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
let errors = _config.default.validate(snapshot, snapshot.domSnapshot ? '/snapshot/dom' : '/snapshot');
|
|
50
|
+
|
|
51
|
+
if (errors) {
|
|
52
|
+
log.warn('Invalid snapshot options:', meta);
|
|
53
|
+
|
|
54
|
+
for (let e of errors) log.warn(`- ${e.path}: ${e.message}`, meta);
|
|
55
|
+
} // inherit options from the percy config
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
return (0, _utils.merge)([config.snapshot, {
|
|
59
|
+
name,
|
|
60
|
+
meta,
|
|
61
|
+
clientInfo,
|
|
62
|
+
environmentInfo,
|
|
63
|
+
// only specific discovery options are used per-snapshot
|
|
64
|
+
discovery: {
|
|
65
|
+
allowedHostnames: [uri.hostname, ...config.discovery.allowedHostnames],
|
|
66
|
+
networkIdleTimeout: config.discovery.networkIdleTimeout,
|
|
67
|
+
requestHeaders: config.discovery.requestHeaders,
|
|
68
|
+
authorization: config.discovery.authorization,
|
|
69
|
+
disableCache: config.discovery.disableCache,
|
|
70
|
+
userAgent: config.discovery.userAgent
|
|
71
|
+
}
|
|
72
|
+
}, snapshot], (path, prev, next) => {
|
|
73
|
+
switch (path.map(k => k.toString()).join('.')) {
|
|
74
|
+
case 'widths':
|
|
75
|
+
// override and sort widths
|
|
76
|
+
return [path, next.sort((a, b) => a - b)];
|
|
77
|
+
|
|
78
|
+
case 'percyCSS':
|
|
79
|
+
// concatenate percy css
|
|
80
|
+
return [path, [prev, next].filter(Boolean).join('\n')];
|
|
81
|
+
|
|
82
|
+
case 'execute':
|
|
83
|
+
// shorthand for execute.beforeSnapshot
|
|
84
|
+
return Array.isArray(next) || typeof next !== 'object' ? [path.concat('beforeSnapshot'), next] : [path];
|
|
85
|
+
} // ensure additional snapshots have complete names
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if (path[0] === 'additionalSnapshots' && path.length === 2) {
|
|
89
|
+
let {
|
|
90
|
+
prefix = '',
|
|
91
|
+
suffix = '',
|
|
92
|
+
...n
|
|
93
|
+
} = next;
|
|
94
|
+
next = {
|
|
95
|
+
name: `${prefix}${name}${suffix}`,
|
|
96
|
+
...n
|
|
97
|
+
};
|
|
98
|
+
return [path, next];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
} // Returns a complete and valid snapshot config object and logs verbose debug logs detailing various
|
|
102
|
+
// snapshot options. When `showInfo` is true, specific messages will be logged as info logs rather
|
|
103
|
+
// than debug logs.
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
function debugSnapshotConfig(snapshot, showInfo) {
|
|
107
|
+
let log = (0, _logger.default)('core:snapshot'); // log snapshot info
|
|
108
|
+
|
|
109
|
+
log.debug('---------', snapshot.meta);
|
|
110
|
+
if (showInfo) log.info(`Snapshot found: ${snapshot.name}`, snapshot.meta);else log.debug(`Handling snapshot: ${snapshot.name}`, snapshot.meta); // will log debug info for an object property if its value is defined
|
|
111
|
+
|
|
112
|
+
let debugProp = (obj, prop, format = String) => {
|
|
113
|
+
let val = prop.split('.').reduce((o, k) => o === null || o === void 0 ? void 0 : o[k], obj);
|
|
114
|
+
|
|
115
|
+
if (val != null) {
|
|
116
|
+
// join formatted array values with a space
|
|
117
|
+
val = [].concat(val).map(format).join(', ');
|
|
118
|
+
log.debug(`- ${prop}: ${val}`, snapshot.meta);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
debugProp(snapshot, 'url');
|
|
123
|
+
debugProp(snapshot, 'widths', v => `${v}px`);
|
|
124
|
+
debugProp(snapshot, 'minHeight', v => `${v}px`);
|
|
125
|
+
debugProp(snapshot, 'enableJavaScript');
|
|
126
|
+
debugProp(snapshot, 'waitForTimeout');
|
|
127
|
+
debugProp(snapshot, 'waitForSelector');
|
|
128
|
+
debugProp(snapshot, 'execute.afterNavigation');
|
|
129
|
+
debugProp(snapshot, 'execute.beforeResize');
|
|
130
|
+
debugProp(snapshot, 'execute.afterResize');
|
|
131
|
+
debugProp(snapshot, 'execute.beforeSnapshot');
|
|
132
|
+
debugProp(snapshot, 'discovery.allowedHostnames');
|
|
133
|
+
debugProp(snapshot, 'discovery.requestHeaders', JSON.stringify);
|
|
134
|
+
debugProp(snapshot, 'discovery.authorization', JSON.stringify);
|
|
135
|
+
debugProp(snapshot, 'discovery.disableCache');
|
|
136
|
+
debugProp(snapshot, 'discovery.userAgent');
|
|
137
|
+
debugProp(snapshot, 'clientInfo');
|
|
138
|
+
debugProp(snapshot, 'environmentInfo');
|
|
139
|
+
debugProp(snapshot, 'domSnapshot', Boolean);
|
|
140
|
+
|
|
141
|
+
for (let added of snapshot.additionalSnapshots || []) {
|
|
142
|
+
if (showInfo) log.info(`Snapshot found: ${added.name}`, snapshot.meta);else log.debug(`Additional snapshot: ${added.name}`, snapshot.meta);
|
|
143
|
+
debugProp(added, 'waitForTimeout');
|
|
144
|
+
debugProp(added, 'waitForSelector');
|
|
145
|
+
debugProp(added, 'execute');
|
|
146
|
+
}
|
|
147
|
+
} // Calls the provided callback with additional resources
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
function handleSnapshotResources(snapshot, map, callback) {
|
|
151
|
+
let resources = [...map.values()]; // sort the root resource first
|
|
152
|
+
|
|
153
|
+
let [root] = resources.splice(resources.findIndex(r => r.root), 1);
|
|
154
|
+
resources.unshift(root); // inject Percy CSS
|
|
155
|
+
|
|
156
|
+
if (snapshot.percyCSS) {
|
|
157
|
+
let css = (0, _utils2.createPercyCSSResource)(root.url, snapshot.percyCSS);
|
|
158
|
+
resources.push(css); // replace root contents and associated properties
|
|
159
|
+
|
|
160
|
+
Object.assign(root, (0, _utils2.createRootResource)(root.url, root.content.replace(/(<\/body>)(?!.*\1)/is, `<link data-percy-specific-css rel="stylesheet" href="${css.pathname}"/>` + '$&')));
|
|
161
|
+
} // include associated snapshot logs matched by meta information
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
resources.push((0, _utils2.createLogResource)(_logger.default.query(log => {
|
|
165
|
+
var _log$meta$snapshot;
|
|
166
|
+
|
|
167
|
+
return ((_log$meta$snapshot = log.meta.snapshot) === null || _log$meta$snapshot === void 0 ? void 0 : _log$meta$snapshot.name) === snapshot.meta.snapshot.name;
|
|
168
|
+
})));
|
|
169
|
+
return callback(snapshot, resources);
|
|
170
|
+
} // Wait for a page's asset discovery network to idle
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
function waitForDiscoveryNetworkIdle(page, options) {
|
|
174
|
+
let {
|
|
175
|
+
allowedHostnames,
|
|
176
|
+
networkIdleTimeout
|
|
177
|
+
} = options;
|
|
178
|
+
|
|
179
|
+
let filter = r => (0, _utils2.hostnameMatches)(allowedHostnames, r.url);
|
|
180
|
+
|
|
181
|
+
return page.network.idle(filter, networkIdleTimeout);
|
|
182
|
+
} // Used to cache resources across core instances
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
const RESOURCE_CACHE_KEY = Symbol('resource-cache'); // Discovers resources for a snapshot using a browser page to intercept requests. The callback
|
|
186
|
+
// function will be called with the snapshot name (for additional snapshots) and an array of
|
|
187
|
+
// discovered resources. When additional snapshots are provided, the callback will be called once
|
|
188
|
+
// for each snapshot.
|
|
189
|
+
|
|
190
|
+
async function* discoverSnapshotResources(percy, snapshot, callback) {
|
|
191
|
+
var _snapshot$enableJavaS;
|
|
192
|
+
|
|
193
|
+
debugSnapshotConfig(snapshot, percy.dryRun); // when dry-running, invoke the callback for each snapshot and immediately return
|
|
194
|
+
|
|
195
|
+
let allSnapshots = [snapshot, ...(snapshot.additionalSnapshots || [])];
|
|
196
|
+
if (percy.dryRun) return allSnapshots.map(s => callback(s)); // keep a global resource cache across snapshots
|
|
197
|
+
|
|
198
|
+
let cache = percy[RESOURCE_CACHE_KEY] || (percy[RESOURCE_CACHE_KEY] = new Map()); // copy widths to prevent mutation later
|
|
199
|
+
|
|
200
|
+
let widths = snapshot.widths.slice(); // preload the root resource for existing dom snapshots
|
|
201
|
+
|
|
202
|
+
let resources = new Map(snapshot.domSnapshot && [(0, _utils2.createRootResource)(snapshot.url, snapshot.domSnapshot)].map(resource => [resource.url, resource])); // open a new browser page
|
|
203
|
+
|
|
204
|
+
let page = yield percy.browser.page({
|
|
205
|
+
enableJavaScript: (_snapshot$enableJavaS = snapshot.enableJavaScript) !== null && _snapshot$enableJavaS !== void 0 ? _snapshot$enableJavaS : !snapshot.domSnapshot,
|
|
206
|
+
networkIdleTimeout: snapshot.discovery.networkIdleTimeout,
|
|
207
|
+
requestHeaders: snapshot.discovery.requestHeaders,
|
|
208
|
+
authorization: snapshot.discovery.authorization,
|
|
209
|
+
userAgent: snapshot.discovery.userAgent,
|
|
210
|
+
meta: snapshot.meta,
|
|
211
|
+
// enable network inteception
|
|
212
|
+
intercept: {
|
|
213
|
+
enableJavaScript: snapshot.enableJavaScript,
|
|
214
|
+
disableCache: snapshot.discovery.disableCache,
|
|
215
|
+
allowedHostnames: snapshot.discovery.allowedHostnames,
|
|
216
|
+
getResource: u => resources.get(u) || cache.get(u),
|
|
217
|
+
saveResource: r => resources.set(r.url, r) && cache.set(r.url, r)
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
var _snapshot$execute;
|
|
223
|
+
|
|
224
|
+
// set the initial page size
|
|
225
|
+
yield page.resize({
|
|
226
|
+
width: widths.shift(),
|
|
227
|
+
height: snapshot.minHeight
|
|
228
|
+
}); // navigate to the url
|
|
229
|
+
|
|
230
|
+
yield page.goto(snapshot.url);
|
|
231
|
+
yield page.evaluate((_snapshot$execute = snapshot.execute) === null || _snapshot$execute === void 0 ? void 0 : _snapshot$execute.afterNavigation); // trigger resize events for other widths
|
|
232
|
+
|
|
233
|
+
for (let width of widths) {
|
|
234
|
+
var _snapshot$execute2, _snapshot$execute3;
|
|
235
|
+
|
|
236
|
+
yield page.evaluate((_snapshot$execute2 = snapshot.execute) === null || _snapshot$execute2 === void 0 ? void 0 : _snapshot$execute2.beforeResize);
|
|
237
|
+
yield waitForDiscoveryNetworkIdle(page, snapshot.discovery);
|
|
238
|
+
yield page.resize({
|
|
239
|
+
width,
|
|
240
|
+
height: snapshot.minHeight
|
|
241
|
+
});
|
|
242
|
+
yield page.evaluate((_snapshot$execute3 = snapshot.execute) === null || _snapshot$execute3 === void 0 ? void 0 : _snapshot$execute3.afterResize);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (snapshot.domSnapshot) {
|
|
246
|
+
// ensure discovery has finished and handle resources
|
|
247
|
+
yield waitForDiscoveryNetworkIdle(page, snapshot.discovery);
|
|
248
|
+
handleSnapshotResources(snapshot, resources, callback);
|
|
249
|
+
} else {
|
|
250
|
+
// capture snapshots sequentially
|
|
251
|
+
for (let snap of allSnapshots) {
|
|
252
|
+
// shallow merge snapshot options
|
|
253
|
+
let options = { ...snapshot,
|
|
254
|
+
...snap
|
|
255
|
+
}; // will wait for timeouts, selectors, and additional network activity
|
|
256
|
+
|
|
257
|
+
let {
|
|
258
|
+
url,
|
|
259
|
+
dom
|
|
260
|
+
} = yield page.snapshot(options); // handle resources and remove previously captured dom snapshots
|
|
261
|
+
|
|
262
|
+
resources.set(url, (0, _utils2.createRootResource)(url, dom));
|
|
263
|
+
handleSnapshotResources(options, resources, callback);
|
|
264
|
+
resources.delete(url);
|
|
265
|
+
}
|
|
266
|
+
} // page clean up
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
await page.close();
|
|
270
|
+
} catch (error) {
|
|
271
|
+
await page.close();
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
}
|
package/dist/utils.js
CHANGED
|
@@ -3,20 +3,19 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.hostname = hostname;
|
|
7
|
-
exports.normalizeURL = normalizeURL;
|
|
8
|
-
exports.createResource = createResource;
|
|
9
|
-
exports.createRootResource = createRootResource;
|
|
10
6
|
exports.createLogResource = createLogResource;
|
|
11
7
|
exports.createPercyCSSResource = createPercyCSSResource;
|
|
12
|
-
exports.
|
|
13
|
-
exports.
|
|
8
|
+
exports.createResource = createResource;
|
|
9
|
+
exports.createRootResource = createRootResource;
|
|
10
|
+
exports.hostname = hostname;
|
|
14
11
|
Object.defineProperty(exports, "hostnameMatches", {
|
|
15
12
|
enumerable: true,
|
|
16
13
|
get: function () {
|
|
17
14
|
return _utils.hostnameMatches;
|
|
18
15
|
}
|
|
19
16
|
});
|
|
17
|
+
exports.normalizeURL = normalizeURL;
|
|
18
|
+
exports.waitFor = waitFor;
|
|
20
19
|
|
|
21
20
|
var _utils = require("@percy/client/dist/utils");
|
|
22
21
|
|
|
@@ -53,33 +52,22 @@ function createRootResource(url, content) {
|
|
|
53
52
|
return createResource(normalizeURL(url), content, 'text/html', {
|
|
54
53
|
root: true
|
|
55
54
|
});
|
|
56
|
-
} // Creates a log resource object.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
function createLogResource(logs) {
|
|
60
|
-
return createResource(`/percy.${Date.now()}.log`, JSON.stringify(logs), 'text/plain');
|
|
61
55
|
} // Creates a Percy CSS resource object.
|
|
62
56
|
|
|
63
57
|
|
|
64
58
|
function createPercyCSSResource(url, css) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
} // returns a new root resource with the injected Percy CSS
|
|
59
|
+
let {
|
|
60
|
+
href,
|
|
61
|
+
pathname
|
|
62
|
+
} = new URL(`/percy-specific.${Date.now()}.css`, url);
|
|
63
|
+
return createResource(href, css, 'text/css', {
|
|
64
|
+
pathname
|
|
65
|
+
});
|
|
66
|
+
} // Creates a log resource object.
|
|
75
67
|
|
|
76
68
|
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
return createRootResource(root.url, root.content.replace(/(<\/body>)(?!.*\1)/is, `<link data-percy-specific-css rel="stylesheet" href="${percyCSS.pathname}"/>` + '$&'));
|
|
80
|
-
} else {
|
|
81
|
-
return root;
|
|
82
|
-
}
|
|
69
|
+
function createLogResource(logs) {
|
|
70
|
+
return createResource(`/percy.${Date.now()}.log`, JSON.stringify(logs), 'text/plain');
|
|
83
71
|
} // Polls for the predicate to be truthy within a timeout or the returned promise rejects. If
|
|
84
72
|
// the second argument is an options object and `idle` is provided, the predicate will be
|
|
85
73
|
// checked again after the idle period. This helper is injected as an argument when using
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/core",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.71",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"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.
|
|
28
|
+
"@percy/client": "1.0.0-beta.71",
|
|
29
|
+
"@percy/config": "1.0.0-beta.71",
|
|
30
|
+
"@percy/dom": "1.0.0-beta.71",
|
|
31
|
+
"@percy/logger": "1.0.0-beta.71",
|
|
32
32
|
"cross-spawn": "^7.0.3",
|
|
33
33
|
"extract-zip": "^2.0.1",
|
|
34
34
|
"rimraf": "^3.0.2",
|
|
@@ -39,5 +39,5 @@
|
|
|
39
39
|
"url": "https://github.com/percy/cli",
|
|
40
40
|
"directory": "packages/core"
|
|
41
41
|
},
|
|
42
|
-
"gitHead": "
|
|
42
|
+
"gitHead": "364d1df717fb19a26ccb024458df6e78a9c11f99"
|
|
43
43
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -44,18 +44,24 @@ export interface SnapshotOptions extends CommonSnapshotOptions {
|
|
|
44
44
|
discovery?: DiscoveryOptions;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
token?: string,
|
|
47
|
+
type ClientEnvInfo = {
|
|
49
48
|
clientInfo?: string,
|
|
50
|
-
environmentInfo?: string
|
|
49
|
+
environmentInfo?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type PercyConfigOptions<C = Pojo> = C & {
|
|
53
|
+
snapshot?: CommonSnapshotOptions,
|
|
54
|
+
discovery?: AllDiscoveryOptions
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type PercyOptions<C = Pojo> = {
|
|
58
|
+
token?: string,
|
|
51
59
|
server?: boolean,
|
|
52
60
|
port?: number,
|
|
53
61
|
concurrency?: number,
|
|
54
62
|
loglevel?: LogLevel,
|
|
55
|
-
config?: undefined | string | false
|
|
56
|
-
|
|
57
|
-
discovery?: AllDiscoveryOptions
|
|
58
|
-
};
|
|
63
|
+
config?: undefined | string | false
|
|
64
|
+
} & ClientEnvInfo & PercyConfigOptions<C>;
|
|
59
65
|
|
|
60
66
|
type SnapshotExec = () => void | Promise<void>;
|
|
61
67
|
|
|
@@ -70,6 +76,8 @@ declare class Percy {
|
|
|
70
76
|
constructor(options?: PercyOptions);
|
|
71
77
|
loglevel(): LogLevel;
|
|
72
78
|
loglevel(level: LogLevel): void;
|
|
79
|
+
config: PercyConfigOptions;
|
|
80
|
+
setConfig(config: ClientEnvInfo & PercyConfigOptions): PercyConfigOptions;
|
|
73
81
|
start(): Promise<void>;
|
|
74
82
|
stop(force?: boolean): Promise<void>;
|
|
75
83
|
idle(): Promise<void>;
|