@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/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;