@percy/core 1.0.0-beta.76 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -18
- package/package.json +24 -14
- package/dist/api.js +0 -76
- package/dist/browser.js +0 -363
- package/dist/config.js +0 -358
- package/dist/discovery.js +0 -130
- package/dist/index.js +0 -15
- package/dist/install.js +0 -173
- package/dist/network.js +0 -365
- package/dist/page.js +0 -293
- package/dist/percy.js +0 -427
- package/dist/queue.js +0 -196
- package/dist/server.js +0 -471
- package/dist/session.js +0 -140
- package/dist/snapshot.js +0 -279
- package/dist/utils.js +0 -170
- package/post-install.js +0 -23
- package/test/helpers/server.js +0 -35
- package/types/index.d.ts +0 -101
package/dist/config.js
DELETED
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.configMigration = configMigration;
|
|
7
|
-
exports.snapshotDOMSchema = exports.schemas = exports.migrations = exports.configSchema = void 0;
|
|
8
|
-
exports.snapshotMigration = snapshotMigration;
|
|
9
|
-
exports.snapshotSchema = void 0;
|
|
10
|
-
// Common config options used in Percy commands
|
|
11
|
-
const configSchema = {
|
|
12
|
-
snapshot: {
|
|
13
|
-
type: 'object',
|
|
14
|
-
additionalProperties: false,
|
|
15
|
-
properties: {
|
|
16
|
-
widths: {
|
|
17
|
-
type: 'array',
|
|
18
|
-
default: [375, 1280],
|
|
19
|
-
items: {
|
|
20
|
-
type: 'integer',
|
|
21
|
-
maximum: 2000,
|
|
22
|
-
minimum: 10
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
minHeight: {
|
|
26
|
-
type: 'integer',
|
|
27
|
-
default: 1024,
|
|
28
|
-
maximum: 2000,
|
|
29
|
-
minimum: 10
|
|
30
|
-
},
|
|
31
|
-
percyCSS: {
|
|
32
|
-
type: 'string',
|
|
33
|
-
default: ''
|
|
34
|
-
},
|
|
35
|
-
enableJavaScript: {
|
|
36
|
-
type: 'boolean'
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
discovery: {
|
|
41
|
-
type: 'object',
|
|
42
|
-
additionalProperties: false,
|
|
43
|
-
properties: {
|
|
44
|
-
allowedHostnames: {
|
|
45
|
-
type: 'array',
|
|
46
|
-
default: [],
|
|
47
|
-
items: {
|
|
48
|
-
type: 'string',
|
|
49
|
-
allOf: [{
|
|
50
|
-
not: {
|
|
51
|
-
pattern: '[^/]/'
|
|
52
|
-
},
|
|
53
|
-
error: 'must not include a pathname'
|
|
54
|
-
}, {
|
|
55
|
-
not: {
|
|
56
|
-
pattern: '^([a-zA-Z]+:)?//'
|
|
57
|
-
},
|
|
58
|
-
error: 'must not include a protocol'
|
|
59
|
-
}]
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
networkIdleTimeout: {
|
|
63
|
-
type: 'integer',
|
|
64
|
-
default: 100,
|
|
65
|
-
maximum: 750,
|
|
66
|
-
minimum: 1
|
|
67
|
-
},
|
|
68
|
-
disableCache: {
|
|
69
|
-
type: 'boolean'
|
|
70
|
-
},
|
|
71
|
-
requestHeaders: {
|
|
72
|
-
type: 'object',
|
|
73
|
-
normalize: false,
|
|
74
|
-
additionalProperties: {
|
|
75
|
-
type: 'string'
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
authorization: {
|
|
79
|
-
type: 'object',
|
|
80
|
-
additionalProperties: false,
|
|
81
|
-
properties: {
|
|
82
|
-
username: {
|
|
83
|
-
type: 'string'
|
|
84
|
-
},
|
|
85
|
-
password: {
|
|
86
|
-
type: 'string'
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
cookies: {
|
|
91
|
-
anyOf: [{
|
|
92
|
-
type: 'object',
|
|
93
|
-
normalize: false,
|
|
94
|
-
additionalProperties: {
|
|
95
|
-
type: 'string'
|
|
96
|
-
}
|
|
97
|
-
}, {
|
|
98
|
-
type: 'array',
|
|
99
|
-
normalize: false,
|
|
100
|
-
items: {
|
|
101
|
-
type: 'object',
|
|
102
|
-
required: ['name', 'value'],
|
|
103
|
-
properties: {
|
|
104
|
-
name: {
|
|
105
|
-
type: 'string'
|
|
106
|
-
},
|
|
107
|
-
value: {
|
|
108
|
-
type: 'string'
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}]
|
|
113
|
-
},
|
|
114
|
-
userAgent: {
|
|
115
|
-
type: 'string'
|
|
116
|
-
},
|
|
117
|
-
concurrency: {
|
|
118
|
-
type: 'integer',
|
|
119
|
-
minimum: 1
|
|
120
|
-
},
|
|
121
|
-
launchOptions: {
|
|
122
|
-
type: 'object',
|
|
123
|
-
additionalProperties: false,
|
|
124
|
-
properties: {
|
|
125
|
-
executable: {
|
|
126
|
-
type: 'string'
|
|
127
|
-
},
|
|
128
|
-
timeout: {
|
|
129
|
-
type: 'integer'
|
|
130
|
-
},
|
|
131
|
-
args: {
|
|
132
|
-
type: 'array',
|
|
133
|
-
items: {
|
|
134
|
-
type: 'string'
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
headless: {
|
|
138
|
-
type: 'boolean'
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}; // Common per-snapshot capture options
|
|
145
|
-
|
|
146
|
-
exports.configSchema = configSchema;
|
|
147
|
-
const snapshotSchema = {
|
|
148
|
-
$id: '/snapshot',
|
|
149
|
-
type: 'object',
|
|
150
|
-
required: ['url'],
|
|
151
|
-
additionalProperties: false,
|
|
152
|
-
$refs: {
|
|
153
|
-
exec: {
|
|
154
|
-
oneOf: [{
|
|
155
|
-
oneOf: [{
|
|
156
|
-
type: 'string'
|
|
157
|
-
}, {
|
|
158
|
-
instanceof: 'Function'
|
|
159
|
-
}]
|
|
160
|
-
}, {
|
|
161
|
-
type: 'array',
|
|
162
|
-
items: {
|
|
163
|
-
$ref: '#/$refs/exec/oneOf/0'
|
|
164
|
-
}
|
|
165
|
-
}]
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
properties: {
|
|
169
|
-
url: {
|
|
170
|
-
type: 'string'
|
|
171
|
-
},
|
|
172
|
-
name: {
|
|
173
|
-
type: 'string'
|
|
174
|
-
},
|
|
175
|
-
widths: {
|
|
176
|
-
$ref: '/config/snapshot#/properties/widths'
|
|
177
|
-
},
|
|
178
|
-
minHeight: {
|
|
179
|
-
$ref: '/config/snapshot#/properties/minHeight'
|
|
180
|
-
},
|
|
181
|
-
percyCSS: {
|
|
182
|
-
$ref: '/config/snapshot#/properties/percyCSS'
|
|
183
|
-
},
|
|
184
|
-
enableJavaScript: {
|
|
185
|
-
$ref: '/config/snapshot#/properties/enableJavaScript'
|
|
186
|
-
},
|
|
187
|
-
discovery: {
|
|
188
|
-
type: 'object',
|
|
189
|
-
additionalProperties: false,
|
|
190
|
-
properties: {
|
|
191
|
-
allowedHostnames: {
|
|
192
|
-
$ref: '/config/discovery#/properties/allowedHostnames'
|
|
193
|
-
},
|
|
194
|
-
requestHeaders: {
|
|
195
|
-
$ref: '/config/discovery#/properties/requestHeaders'
|
|
196
|
-
},
|
|
197
|
-
authorization: {
|
|
198
|
-
$ref: '/config/discovery#/properties/authorization'
|
|
199
|
-
},
|
|
200
|
-
disableCache: {
|
|
201
|
-
$ref: '/config/discovery#/properties/disableCache'
|
|
202
|
-
},
|
|
203
|
-
userAgent: {
|
|
204
|
-
$ref: '/config/discovery#/properties/userAgent'
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
waitForSelector: {
|
|
209
|
-
type: 'string'
|
|
210
|
-
},
|
|
211
|
-
waitForTimeout: {
|
|
212
|
-
type: 'integer',
|
|
213
|
-
minimum: 1,
|
|
214
|
-
maximum: 30000
|
|
215
|
-
},
|
|
216
|
-
execute: {
|
|
217
|
-
oneOf: [{
|
|
218
|
-
$ref: '/snapshot#/$refs/exec'
|
|
219
|
-
}, {
|
|
220
|
-
type: 'object',
|
|
221
|
-
additionalProperties: false,
|
|
222
|
-
properties: {
|
|
223
|
-
afterNavigation: {
|
|
224
|
-
$ref: '/snapshot#/$refs/exec'
|
|
225
|
-
},
|
|
226
|
-
beforeResize: {
|
|
227
|
-
$ref: '/snapshot#/$refs/exec'
|
|
228
|
-
},
|
|
229
|
-
afterResize: {
|
|
230
|
-
$ref: '/snapshot#/$refs/exec'
|
|
231
|
-
},
|
|
232
|
-
beforeSnapshot: {
|
|
233
|
-
$ref: '/snapshot#/$refs/exec'
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}]
|
|
237
|
-
},
|
|
238
|
-
additionalSnapshots: {
|
|
239
|
-
type: 'array',
|
|
240
|
-
items: {
|
|
241
|
-
type: 'object',
|
|
242
|
-
additionalProperties: false,
|
|
243
|
-
oneOf: [{
|
|
244
|
-
required: ['name']
|
|
245
|
-
}, {
|
|
246
|
-
anyOf: [{
|
|
247
|
-
required: ['prefix']
|
|
248
|
-
}, {
|
|
249
|
-
required: ['suffix']
|
|
250
|
-
}]
|
|
251
|
-
}],
|
|
252
|
-
properties: {
|
|
253
|
-
prefix: {
|
|
254
|
-
type: 'string'
|
|
255
|
-
},
|
|
256
|
-
suffix: {
|
|
257
|
-
type: 'string'
|
|
258
|
-
},
|
|
259
|
-
name: {
|
|
260
|
-
$ref: '/snapshot#/properties/name'
|
|
261
|
-
},
|
|
262
|
-
waitForTimeout: {
|
|
263
|
-
$ref: '/snapshot#/properties/waitForTimeout'
|
|
264
|
-
},
|
|
265
|
-
waitForSelector: {
|
|
266
|
-
$ref: '/snapshot#/properties/waitForSelector'
|
|
267
|
-
},
|
|
268
|
-
execute: {
|
|
269
|
-
$ref: '/snapshot#/$refs/exec'
|
|
270
|
-
}
|
|
271
|
-
},
|
|
272
|
-
errors: {
|
|
273
|
-
oneOf: ({
|
|
274
|
-
params
|
|
275
|
-
}) => params.passingSchemas ? 'prefix & suffix are ignored when a name is provided' : 'missing required name, prefix, or suffix'
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}; // Disallow capture options for dom snapshots
|
|
281
|
-
|
|
282
|
-
exports.snapshotSchema = snapshotSchema;
|
|
283
|
-
const snapshotDOMSchema = {
|
|
284
|
-
$id: '/snapshot/dom',
|
|
285
|
-
type: 'object',
|
|
286
|
-
additionalProperties: false,
|
|
287
|
-
required: ['url', 'domSnapshot'],
|
|
288
|
-
disallowed: ['additionalSnapshots', 'waitForTimeout', 'waitForSelector', 'execute'],
|
|
289
|
-
errors: {
|
|
290
|
-
disallowed: 'not accepted with DOM snapshots'
|
|
291
|
-
},
|
|
292
|
-
properties: {
|
|
293
|
-
domSnapshot: {
|
|
294
|
-
type: 'string'
|
|
295
|
-
},
|
|
296
|
-
// schemas have no concept of inheritance, but we can leverage JS for brevity
|
|
297
|
-
...snapshotSchema.properties
|
|
298
|
-
}
|
|
299
|
-
}; // Grouped schemas for easier registration
|
|
300
|
-
|
|
301
|
-
exports.snapshotDOMSchema = snapshotDOMSchema;
|
|
302
|
-
const schemas = [configSchema, snapshotSchema, snapshotDOMSchema]; // Config migrate function
|
|
303
|
-
|
|
304
|
-
exports.schemas = schemas;
|
|
305
|
-
|
|
306
|
-
function configMigration(config, util) {
|
|
307
|
-
/* eslint-disable curly */
|
|
308
|
-
if (config.version < 2) {
|
|
309
|
-
// discovery options have moved
|
|
310
|
-
util.map('agent.assetDiscovery.allowedHostnames', 'discovery.allowedHostnames');
|
|
311
|
-
util.map('agent.assetDiscovery.networkIdleTimeout', 'discovery.networkIdleTimeout');
|
|
312
|
-
util.map('agent.assetDiscovery.cacheResponses', 'discovery.disableCache', v => !v);
|
|
313
|
-
util.map('agent.assetDiscovery.requestHeaders', 'discovery.requestHeaders');
|
|
314
|
-
util.map('agent.assetDiscovery.pagePoolSizeMax', 'discovery.concurrency');
|
|
315
|
-
util.del('agent');
|
|
316
|
-
} else {
|
|
317
|
-
let notice = {
|
|
318
|
-
type: 'config',
|
|
319
|
-
until: '1.0.0'
|
|
320
|
-
}; // snapshot discovery options have moved
|
|
321
|
-
|
|
322
|
-
util.deprecate('snapshot.authorization', {
|
|
323
|
-
map: 'discovery.authorization',
|
|
324
|
-
...notice
|
|
325
|
-
});
|
|
326
|
-
util.deprecate('snapshot.requestHeaders', {
|
|
327
|
-
map: 'discovery.requestHeaders',
|
|
328
|
-
...notice
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
} // Snapshot option migrate function
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
function snapshotMigration(config, util) {
|
|
335
|
-
let notice = {
|
|
336
|
-
type: 'snapshot',
|
|
337
|
-
until: '1.0.0',
|
|
338
|
-
warn: true
|
|
339
|
-
}; // discovery options have moved
|
|
340
|
-
|
|
341
|
-
util.deprecate('authorization', {
|
|
342
|
-
map: 'discovery.authorization',
|
|
343
|
-
...notice
|
|
344
|
-
});
|
|
345
|
-
util.deprecate('requestHeaders', {
|
|
346
|
-
map: 'discovery.requestHeaders',
|
|
347
|
-
...notice
|
|
348
|
-
}); // snapshots option was renamed
|
|
349
|
-
|
|
350
|
-
util.deprecate('snapshots', {
|
|
351
|
-
map: 'additionalSnapshots',
|
|
352
|
-
...notice
|
|
353
|
-
});
|
|
354
|
-
} // Grouped migrations for easier registration
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const migrations = [['/config', configMigration], ['/snapshot', snapshotMigration]];
|
|
358
|
-
exports.migrations = migrations;
|
package/dist/discovery.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.createRequestFailedHandler = createRequestFailedHandler;
|
|
7
|
-
exports.createRequestFinishedHandler = createRequestFinishedHandler;
|
|
8
|
-
exports.createRequestHandler = createRequestHandler;
|
|
9
|
-
|
|
10
|
-
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
11
|
-
|
|
12
|
-
var _utils = require("./utils");
|
|
13
|
-
|
|
14
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
-
|
|
16
|
-
const MAX_RESOURCE_SIZE = 15 * 1024 ** 2; // 15MB
|
|
17
|
-
|
|
18
|
-
const ALLOWED_STATUSES = [200, 201, 301, 302, 304, 307, 308];
|
|
19
|
-
const ALLOWED_RESOURCES = ['Document', 'Stylesheet', 'Image', 'Media', 'Font', 'Other'];
|
|
20
|
-
|
|
21
|
-
function createRequestHandler(network, {
|
|
22
|
-
disableCache,
|
|
23
|
-
getResource
|
|
24
|
-
}) {
|
|
25
|
-
let log = (0, _logger.default)('core:discovery');
|
|
26
|
-
return async request => {
|
|
27
|
-
let url = request.url;
|
|
28
|
-
let meta = { ...network.meta,
|
|
29
|
-
url
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
log.debug(`Handling request: ${url}`, meta);
|
|
34
|
-
let resource = getResource(url);
|
|
35
|
-
|
|
36
|
-
if (resource !== null && resource !== void 0 && resource.root) {
|
|
37
|
-
log.debug('- Serving root resource', meta);
|
|
38
|
-
await request.respond(resource);
|
|
39
|
-
} else if (resource && !disableCache) {
|
|
40
|
-
log.debug('- Resource cache hit', meta);
|
|
41
|
-
await request.respond(resource);
|
|
42
|
-
} else {
|
|
43
|
-
await request.continue();
|
|
44
|
-
}
|
|
45
|
-
} catch (error) {
|
|
46
|
-
log.debug(`Encountered an error handling request: ${url}`, meta);
|
|
47
|
-
log.debug(error);
|
|
48
|
-
/* istanbul ignore next: race condition */
|
|
49
|
-
|
|
50
|
-
await request.abort(error).catch(e => log.debug(e, meta));
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function createRequestFinishedHandler(network, {
|
|
56
|
-
enableJavaScript,
|
|
57
|
-
allowedHostnames,
|
|
58
|
-
disableCache,
|
|
59
|
-
getResource,
|
|
60
|
-
saveResource
|
|
61
|
-
}) {
|
|
62
|
-
let log = (0, _logger.default)('core:discovery');
|
|
63
|
-
return async request => {
|
|
64
|
-
let origin = request.redirectChain[0] || request;
|
|
65
|
-
let url = (0, _utils.normalizeURL)(origin.url);
|
|
66
|
-
let meta = { ...network.meta,
|
|
67
|
-
url
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
var _resource;
|
|
72
|
-
|
|
73
|
-
let resource = getResource(url); // process and cache the response and resource
|
|
74
|
-
|
|
75
|
-
if (!((_resource = resource) !== null && _resource !== void 0 && _resource.root) && (!resource || disableCache)) {
|
|
76
|
-
let response = request.response;
|
|
77
|
-
let capture = response && (0, _utils.hostnameMatches)(allowedHostnames, url);
|
|
78
|
-
let body = capture && (await response.buffer());
|
|
79
|
-
log.debug(`Processing resource: ${url}`, meta);
|
|
80
|
-
/* istanbul ignore next: sanity check */
|
|
81
|
-
|
|
82
|
-
if (!response) {
|
|
83
|
-
return log.debug('- Skipping no response', meta);
|
|
84
|
-
} else if (!capture) {
|
|
85
|
-
return log.debug('- Skipping remote resource', meta);
|
|
86
|
-
} else if (!body.length) {
|
|
87
|
-
return log.debug('- Skipping empty response', meta);
|
|
88
|
-
} else if (body.length > MAX_RESOURCE_SIZE) {
|
|
89
|
-
return log.debug('- Skipping resource larger than 15MB', meta);
|
|
90
|
-
} else if (!ALLOWED_STATUSES.includes(response.status)) {
|
|
91
|
-
return log.debug(`- Skipping disallowed status [${response.status}]`, meta);
|
|
92
|
-
} else if (!enableJavaScript && !ALLOWED_RESOURCES.includes(request.type)) {
|
|
93
|
-
return log.debug(`- Skipping disallowed resource type [${request.type}]`, meta);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
resource = (0, _utils.createResource)(url, body, response.mimeType, {
|
|
97
|
-
status: response.status,
|
|
98
|
-
// 'Network.responseReceived' returns headers split by newlines, however
|
|
99
|
-
// `Fetch.fulfillRequest` (used for cached responses) will hang with newlines.
|
|
100
|
-
headers: Object.entries(response.headers).reduce((norm, [key, value]) => Object.assign(norm, {
|
|
101
|
-
[key]: value.split('\n')
|
|
102
|
-
}), {})
|
|
103
|
-
});
|
|
104
|
-
log.debug(`- sha: ${resource.sha}`, meta);
|
|
105
|
-
log.debug(`- mimetype: ${resource.mimetype}`, meta);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
saveResource(resource);
|
|
109
|
-
} catch (error) {
|
|
110
|
-
log.debug(`Encountered an error processing resource: ${url}`, meta);
|
|
111
|
-
log.debug(error);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function createRequestFailedHandler(network) {
|
|
117
|
-
let log = (0, _logger.default)('core:discovery');
|
|
118
|
-
return ({
|
|
119
|
-
url,
|
|
120
|
-
error
|
|
121
|
-
}) => {
|
|
122
|
-
// do not log generic failures since the real error was most likely
|
|
123
|
-
// already logged from elsewhere
|
|
124
|
-
if (error !== 'net::ERR_FAILED') {
|
|
125
|
-
log.debug(`Request failed for ${url}: ${error}`, { ...network.meta,
|
|
126
|
-
url
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
}
|
package/dist/index.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const PercyConfig = require('@percy/config');
|
|
4
|
-
|
|
5
|
-
const CoreConfig = require('./config');
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
Percy
|
|
9
|
-
} = require('./percy');
|
|
10
|
-
|
|
11
|
-
PercyConfig.addSchema(CoreConfig.schemas);
|
|
12
|
-
PercyConfig.addMigration(CoreConfig.migrations); // export the Percy class with commonjs compatibility
|
|
13
|
-
|
|
14
|
-
module.exports = Percy;
|
|
15
|
-
module.exports.Percy = Percy;
|
package/dist/install.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
4
|
-
|
|
5
|
-
var _path = _interopRequireDefault(require("path"));
|
|
6
|
-
|
|
7
|
-
var _https = _interopRequireDefault(require("https"));
|
|
8
|
-
|
|
9
|
-
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
10
|
-
|
|
11
|
-
var _request = require("@percy/client/dist/request");
|
|
12
|
-
|
|
13
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
|
-
|
|
15
|
-
// Formats a raw byte integer as a string
|
|
16
|
-
function formatBytes(int) {
|
|
17
|
-
let units = ['kB', 'MB', 'GB'];
|
|
18
|
-
let base = 1024;
|
|
19
|
-
let u = -1;
|
|
20
|
-
if (Math.abs(int) < base) return `${int}B`;
|
|
21
|
-
|
|
22
|
-
while (Math.abs(int) >= base && u++ < 2) int /= base;
|
|
23
|
-
|
|
24
|
-
return `${int.toFixed(1)}${units[u]}`;
|
|
25
|
-
} // Formats milleseconds as "MM:SS"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
function formatTime(ms) {
|
|
29
|
-
let minutes = (ms / 1000 / 60).toString().split('.')[0].padStart(2, '0');
|
|
30
|
-
let seconds = (ms / 1000 % 60).toFixed().padStart(2, '0');
|
|
31
|
-
return `${minutes}:${seconds}`;
|
|
32
|
-
} // Formats progress as ":prefix [:bar] :ratio :percent :eta"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
function formatProgress(prefix, total, start, progress) {
|
|
36
|
-
let width = 20;
|
|
37
|
-
let ratio = Math.min(Math.max(progress / total, 0), 1);
|
|
38
|
-
let percent = Math.floor(ratio * 100).toFixed(0);
|
|
39
|
-
let barLen = Math.round(width * ratio);
|
|
40
|
-
let barContent = Array(Math.max(0, barLen + 1)).join('=') + Array(Math.max(0, width - barLen + 1)).join(' ');
|
|
41
|
-
let elapsed = Date.now() - start;
|
|
42
|
-
let eta = ratio >= 1 ? 0 : elapsed * (total / progress - 1);
|
|
43
|
-
return `${prefix} [${barContent}] ` + `${formatBytes(progress)}/${formatBytes(total)} ` + `${percent}% ${formatTime(eta)}`;
|
|
44
|
-
} // Returns an item from the map keyed by the current platform
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
function selectByPlatform(map) {
|
|
48
|
-
let {
|
|
49
|
-
platform,
|
|
50
|
-
arch
|
|
51
|
-
} = process;
|
|
52
|
-
if (platform === 'win32' && arch === 'x64') platform = 'win64';
|
|
53
|
-
if (platform === 'darwin' && arch === 'arm64') platform = 'darwinArm';
|
|
54
|
-
return map[platform];
|
|
55
|
-
} // Installs a revision of Chromium to a local directory
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
function installChromium({
|
|
59
|
-
// default directory is within @percy/core package root
|
|
60
|
-
directory = _path.default.resolve(__dirname, '../.local-chromium'),
|
|
61
|
-
// default chromium revision by platform (see installChromium.revisions)
|
|
62
|
-
revision = selectByPlatform(installChromium.revisions)
|
|
63
|
-
} = {}) {
|
|
64
|
-
let extract = (i, o) => require('extract-zip')(i, {
|
|
65
|
-
dir: o
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
let url = 'https://storage.googleapis.com/chromium-browser-snapshots/' + selectByPlatform({
|
|
69
|
-
linux: `Linux_x64/${revision}/chrome-linux.zip`,
|
|
70
|
-
darwin: `Mac/${revision}/chrome-mac.zip`,
|
|
71
|
-
darwinArm: `Mac_Arm/${revision}/chrome-mac.zip`,
|
|
72
|
-
win64: `Win_x64/${revision}/chrome-win.zip`,
|
|
73
|
-
win32: `Win/${revision}/chrome-win.zip`
|
|
74
|
-
});
|
|
75
|
-
let executable = selectByPlatform({
|
|
76
|
-
linux: _path.default.join('chrome-linux', 'chrome'),
|
|
77
|
-
win64: _path.default.join('chrome-win', 'chrome.exe'),
|
|
78
|
-
win32: _path.default.join('chrome-win', 'chrome.exe'),
|
|
79
|
-
darwin: _path.default.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'),
|
|
80
|
-
darwinArm: _path.default.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium')
|
|
81
|
-
});
|
|
82
|
-
return install({
|
|
83
|
-
name: 'Chromium',
|
|
84
|
-
revision,
|
|
85
|
-
url,
|
|
86
|
-
extract,
|
|
87
|
-
directory,
|
|
88
|
-
executable
|
|
89
|
-
});
|
|
90
|
-
} // default chromium revisions corresponds to v92.0.4515.x
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
installChromium.revisions = {
|
|
94
|
-
linux: '885264',
|
|
95
|
-
win64: '885282',
|
|
96
|
-
win32: '885263',
|
|
97
|
-
darwin: '885263',
|
|
98
|
-
darwinArm: '885282'
|
|
99
|
-
}; // Installs an executable from a url to a local directory, returning the full path to the extracted
|
|
100
|
-
// binary. Skips installation if the executable already exists at the binary path.
|
|
101
|
-
|
|
102
|
-
async function install({
|
|
103
|
-
name,
|
|
104
|
-
revision,
|
|
105
|
-
url,
|
|
106
|
-
extract,
|
|
107
|
-
directory,
|
|
108
|
-
executable
|
|
109
|
-
}) {
|
|
110
|
-
let outdir = _path.default.join(directory, revision);
|
|
111
|
-
|
|
112
|
-
let archive = _path.default.join(outdir, decodeURIComponent(url.split('/').pop()));
|
|
113
|
-
|
|
114
|
-
let exec = _path.default.join(outdir, executable);
|
|
115
|
-
|
|
116
|
-
if (!_fs.default.existsSync(exec)) {
|
|
117
|
-
let log = (0, _logger.default)('core:install');
|
|
118
|
-
let premsg = `Downloading ${name} ${revision}`;
|
|
119
|
-
log.progress(`${premsg}...`);
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
// ensure the out directory exists
|
|
123
|
-
await _fs.default.promises.mkdir(outdir, {
|
|
124
|
-
recursive: true
|
|
125
|
-
}); // download the file at the given URL
|
|
126
|
-
|
|
127
|
-
await new Promise((resolve, reject) => _https.default.get(url, {
|
|
128
|
-
agent: new _request.ProxyHttpsAgent() // allow proxied requests
|
|
129
|
-
|
|
130
|
-
}, response => {
|
|
131
|
-
// on failure, resume the response before rejecting
|
|
132
|
-
if (response.statusCode !== 200) {
|
|
133
|
-
response.resume();
|
|
134
|
-
reject(new Error(`Download failed: ${response.statusCode} - ${url}`));
|
|
135
|
-
return;
|
|
136
|
-
} // log progress
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (log.shouldLog('info') && _logger.default.stdout.isTTY) {
|
|
140
|
-
let total = parseInt(response.headers['content-length'], 10);
|
|
141
|
-
let start, progress;
|
|
142
|
-
response.on('data', chunk => {
|
|
143
|
-
var _start, _progress;
|
|
144
|
-
|
|
145
|
-
(_start = start) !== null && _start !== void 0 ? _start : start = Date.now();
|
|
146
|
-
progress = ((_progress = progress) !== null && _progress !== void 0 ? _progress : 0) + chunk.length;
|
|
147
|
-
log.progress(formatProgress(premsg, total, start, progress));
|
|
148
|
-
});
|
|
149
|
-
} // pipe the response directly to a file
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
response.pipe(_fs.default.createWriteStream(archive).on('finish', resolve).on('error', reject));
|
|
153
|
-
}).on('error', reject)); // extract the downloaded file
|
|
154
|
-
|
|
155
|
-
await extract(archive, outdir); // log success
|
|
156
|
-
|
|
157
|
-
log.info(`Successfully downloaded ${name} ${revision}`);
|
|
158
|
-
} finally {
|
|
159
|
-
// always cleanup the archive
|
|
160
|
-
if (_fs.default.existsSync(archive)) {
|
|
161
|
-
await _fs.default.promises.unlink(archive);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
} // return the path to the executable
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return exec;
|
|
168
|
-
} // commonjs friendly
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
module.exports = install;
|
|
172
|
-
module.exports.chromium = installChromium;
|
|
173
|
-
module.exports.selectByPlatform = selectByPlatform;
|