@percy/core 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +57 -3
- package/dist/config.js +6 -0
- package/dist/discovery.js +3 -1
- package/dist/page.js +6 -4
- package/dist/percy.js +5 -1
- package/dist/snapshot.js +48 -34
- package/package.json +6 -6
- package/test/helpers/request.js +1 -1
package/dist/api.js
CHANGED
|
@@ -8,7 +8,7 @@ export const PERCY_DOM = createRequire(import.meta.url).resolve('@percy/dom'); /
|
|
|
8
8
|
|
|
9
9
|
export function createPercyServer(percy, port) {
|
|
10
10
|
let pkg = getPackageJSON(import.meta.url);
|
|
11
|
-
|
|
11
|
+
let server = Server.createServer({
|
|
12
12
|
port
|
|
13
13
|
}) // facilitate logger websocket connections
|
|
14
14
|
.websocket('/(logger)?', ws => {
|
|
@@ -29,13 +29,31 @@ export function createPercyServer(percy, port) {
|
|
|
29
29
|
}));
|
|
30
30
|
}) // general middleware
|
|
31
31
|
.route((req, res, next) => {
|
|
32
|
+
var _percy$testing, _percy$testing3, _percy$testing3$api, _percy$testing4, _percy$testing4$api;
|
|
33
|
+
|
|
32
34
|
// treat all request bodies as json
|
|
33
35
|
if (req.body) try {
|
|
34
36
|
req.body = JSON.parse(req.body);
|
|
35
37
|
} catch {} // add version header
|
|
36
38
|
|
|
37
|
-
res.setHeader('Access-Control-Expose-Headers', '*, X-Percy-Core-Version');
|
|
38
|
-
|
|
39
|
+
res.setHeader('Access-Control-Expose-Headers', '*, X-Percy-Core-Version'); // skip or change api version header in testing mode
|
|
40
|
+
|
|
41
|
+
if (((_percy$testing = percy.testing) === null || _percy$testing === void 0 ? void 0 : _percy$testing.version) !== false) {
|
|
42
|
+
var _percy$testing2;
|
|
43
|
+
|
|
44
|
+
res.setHeader('X-Percy-Core-Version', ((_percy$testing2 = percy.testing) === null || _percy$testing2 === void 0 ? void 0 : _percy$testing2.version) ?? pkg.version);
|
|
45
|
+
} // support sabotaging requests in testing mode
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if (((_percy$testing3 = percy.testing) === null || _percy$testing3 === void 0 ? void 0 : (_percy$testing3$api = _percy$testing3.api) === null || _percy$testing3$api === void 0 ? void 0 : _percy$testing3$api[req.url.pathname]) === 'error') {
|
|
49
|
+
return res.json(500, {
|
|
50
|
+
success: false,
|
|
51
|
+
error: 'Error: testing'
|
|
52
|
+
});
|
|
53
|
+
} else if (((_percy$testing4 = percy.testing) === null || _percy$testing4 === void 0 ? void 0 : (_percy$testing4$api = _percy$testing4.api) === null || _percy$testing4$api === void 0 ? void 0 : _percy$testing4$api[req.url.pathname]) === 'disconnect') {
|
|
54
|
+
return req.connection.destroy();
|
|
55
|
+
} // return json errors
|
|
56
|
+
|
|
39
57
|
|
|
40
58
|
return next().catch(e => res.json(e.status ?? 500, {
|
|
41
59
|
build: percy.build,
|
|
@@ -77,6 +95,42 @@ export function createPercyServer(percy, port) {
|
|
|
77
95
|
return res.json(200, {
|
|
78
96
|
success: true
|
|
79
97
|
});
|
|
98
|
+
}); // add test endpoints only in testing mode
|
|
99
|
+
|
|
100
|
+
return !percy.testing ? server : server // manipulates testing mode configuration to trigger specific scenarios
|
|
101
|
+
.route('/test/api/:cmd', ({
|
|
102
|
+
body,
|
|
103
|
+
params: {
|
|
104
|
+
cmd
|
|
105
|
+
}
|
|
106
|
+
}, res) => {
|
|
107
|
+
body = Buffer.isBuffer(body) ? body.toString() : body;
|
|
108
|
+
|
|
109
|
+
if (cmd === 'reset') {
|
|
110
|
+
// the reset command will reset testing mode to its default options
|
|
111
|
+
percy.testing = {};
|
|
112
|
+
} else if (cmd === 'version') {
|
|
113
|
+
// the version command will update the api version header for testing
|
|
114
|
+
percy.testing.version = body;
|
|
115
|
+
} else if (cmd === 'error' || cmd === 'disconnect') {
|
|
116
|
+
// the error or disconnect commands will cause specific endpoints to fail
|
|
117
|
+
percy.testing.api = { ...percy.testing.api,
|
|
118
|
+
[body]: cmd
|
|
119
|
+
};
|
|
120
|
+
} else {
|
|
121
|
+
// 404 for unknown commands
|
|
122
|
+
return res.send(404);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return res.json(200, {
|
|
126
|
+
testing: percy.testing
|
|
127
|
+
});
|
|
128
|
+
}) // returns an array of raw logs from the logger
|
|
129
|
+
.route('get', '/test/logs', (req, res) => res.json(200, {
|
|
130
|
+
logs: Array.from(logger.instance.messages)
|
|
131
|
+
})) // serves a very basic html page for testing snapshots
|
|
132
|
+
.route('get', '/test/snapshot', (req, res) => {
|
|
133
|
+
return res.send(200, 'text/html', '<p>Snapshot Me!</p>');
|
|
80
134
|
});
|
|
81
135
|
} // Create a static server instance with an automatic sitemap
|
|
82
136
|
|
package/dist/config.js
CHANGED
|
@@ -28,6 +28,9 @@ export const configSchema = {
|
|
|
28
28
|
},
|
|
29
29
|
scope: {
|
|
30
30
|
type: 'string'
|
|
31
|
+
},
|
|
32
|
+
devicePixelRatio: {
|
|
33
|
+
type: 'integer'
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
},
|
|
@@ -177,6 +180,9 @@ export const snapshotSchema = {
|
|
|
177
180
|
enableJavaScript: {
|
|
178
181
|
$ref: '/config/snapshot#/properties/enableJavaScript'
|
|
179
182
|
},
|
|
183
|
+
devicePixelRatio: {
|
|
184
|
+
$ref: '/config/snapshot#/properties/devicePixelRatio'
|
|
185
|
+
},
|
|
180
186
|
discovery: {
|
|
181
187
|
type: 'object',
|
|
182
188
|
additionalProperties: false,
|
package/dist/discovery.js
CHANGED
|
@@ -89,8 +89,10 @@ export function createRequestFinishedHandler(network, {
|
|
|
89
89
|
|
|
90
90
|
if (mimeType !== null && mimeType !== void 0 && mimeType.includes('font')) {
|
|
91
91
|
// font responses from the browser may not be properly encoded, so request them directly
|
|
92
|
+
log.debug('- Requesting asset directly');
|
|
92
93
|
body = await makeRequest(response.url, {
|
|
93
|
-
buffer: true
|
|
94
|
+
buffer: true,
|
|
95
|
+
headers: request.headers
|
|
94
96
|
});
|
|
95
97
|
}
|
|
96
98
|
|
package/dist/page.js
CHANGED
|
@@ -29,12 +29,14 @@ export class Page {
|
|
|
29
29
|
|
|
30
30
|
async resize({
|
|
31
31
|
width,
|
|
32
|
-
height
|
|
32
|
+
height,
|
|
33
|
+
deviceScaleFactor = 1,
|
|
34
|
+
mobile = false
|
|
33
35
|
}) {
|
|
34
|
-
this.log.debug(`Resize page to ${width}x${height}`);
|
|
36
|
+
this.log.debug(`Resize page to ${width}x${height} @${deviceScaleFactor}x`);
|
|
35
37
|
await this.session.send('Emulation.setDeviceMetricsOverride', {
|
|
36
|
-
deviceScaleFactor
|
|
37
|
-
mobile
|
|
38
|
+
deviceScaleFactor,
|
|
39
|
+
mobile,
|
|
38
40
|
height,
|
|
39
41
|
width
|
|
40
42
|
});
|
package/dist/percy.js
CHANGED
|
@@ -31,6 +31,8 @@ export class Percy {
|
|
|
31
31
|
skipUploads,
|
|
32
32
|
// implies `skipUploads` and also skips asset discovery
|
|
33
33
|
dryRun,
|
|
34
|
+
// implies `dryRun`, silent logs, and adds extra api endpoints
|
|
35
|
+
testing,
|
|
34
36
|
// configuration filepath
|
|
35
37
|
config,
|
|
36
38
|
// provided to @percy/client
|
|
@@ -44,8 +46,10 @@ export class Percy {
|
|
|
44
46
|
// options which will become accessible via the `.config` property
|
|
45
47
|
...options
|
|
46
48
|
} = {}) {
|
|
49
|
+
if (testing) loglevel = 'silent';
|
|
47
50
|
if (loglevel) this.loglevel(loglevel);
|
|
48
|
-
this.
|
|
51
|
+
this.testing = testing ? {} : null;
|
|
52
|
+
this.dryRun = !!testing || !!dryRun;
|
|
49
53
|
this.skipUploads = this.dryRun || !!skipUploads;
|
|
50
54
|
this.deferUploads = this.skipUploads || !!deferUploads;
|
|
51
55
|
if (this.deferUploads) this.#uploads.stop();
|
package/dist/snapshot.js
CHANGED
|
@@ -260,6 +260,7 @@ function debugSnapshotConfig(snapshot, showInfo) {
|
|
|
260
260
|
debugProp(snapshot, 'widths', v => `${v}px`);
|
|
261
261
|
debugProp(snapshot, 'minHeight', v => `${v}px`);
|
|
262
262
|
debugProp(snapshot, 'enableJavaScript');
|
|
263
|
+
debugProp(snapshot, 'deviceScaleFactor');
|
|
263
264
|
debugProp(snapshot, 'waitForTimeout');
|
|
264
265
|
debugProp(snapshot, 'waitForSelector');
|
|
265
266
|
debugProp(snapshot, 'execute.afterNavigation');
|
|
@@ -320,20 +321,55 @@ function waitForDiscoveryNetworkIdle(page, options) {
|
|
|
320
321
|
} // Used to cache resources across core instances
|
|
321
322
|
|
|
322
323
|
|
|
323
|
-
const RESOURCE_CACHE_KEY = Symbol('resource-cache'); //
|
|
324
|
+
const RESOURCE_CACHE_KEY = Symbol('resource-cache'); // Trigger resource requests for a page by iterating over snapshot widths and calling any provided
|
|
325
|
+
// execute options. Additional resize options may be provided to capture resources mobile resources
|
|
326
|
+
|
|
327
|
+
function* triggerResourceRequests(page, snapshot, options) {
|
|
328
|
+
// copy widths to prevent mutation later
|
|
329
|
+
let [initialWidth, ...widths] = snapshot.widths; // set the initial page size
|
|
330
|
+
|
|
331
|
+
yield page.resize({
|
|
332
|
+
width: initialWidth,
|
|
333
|
+
height: snapshot.minHeight,
|
|
334
|
+
...options
|
|
335
|
+
}); // navigate to the url
|
|
336
|
+
|
|
337
|
+
yield page.goto(snapshot.url);
|
|
338
|
+
|
|
339
|
+
if (snapshot.execute) {
|
|
340
|
+
// when any execute options are provided, inject snapshot options
|
|
341
|
+
|
|
342
|
+
/* istanbul ignore next: cannot detect coverage of injected code */
|
|
343
|
+
yield page.eval((_, s) => window.__PERCY__.snapshot = s, snapshot);
|
|
344
|
+
yield page.evaluate(snapshot.execute.afterNavigation);
|
|
345
|
+
} // trigger resize events for other widths
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
for (let width of widths) {
|
|
349
|
+
var _snapshot$execute, _snapshot$execute2;
|
|
350
|
+
|
|
351
|
+
yield page.evaluate((_snapshot$execute = snapshot.execute) === null || _snapshot$execute === void 0 ? void 0 : _snapshot$execute.beforeResize);
|
|
352
|
+
yield waitForDiscoveryNetworkIdle(page, snapshot.discovery);
|
|
353
|
+
yield page.resize({
|
|
354
|
+
width,
|
|
355
|
+
height: snapshot.minHeight,
|
|
356
|
+
...options
|
|
357
|
+
});
|
|
358
|
+
yield page.evaluate((_snapshot$execute2 = snapshot.execute) === null || _snapshot$execute2 === void 0 ? void 0 : _snapshot$execute2.afterResize);
|
|
359
|
+
}
|
|
360
|
+
} // Discovers resources for a snapshot using a browser page to intercept requests. The callback
|
|
324
361
|
// function will be called with the snapshot name (for additional snapshots) and an array of
|
|
325
362
|
// discovered resources. When additional snapshots are provided, the callback will be called once
|
|
326
363
|
// for each snapshot.
|
|
327
364
|
|
|
365
|
+
|
|
328
366
|
export async function* discoverSnapshotResources(percy, snapshot, callback) {
|
|
329
367
|
debugSnapshotConfig(snapshot, percy.dryRun); // when dry-running, invoke the callback for each snapshot and immediately return
|
|
330
368
|
|
|
331
369
|
let allSnapshots = [snapshot, ...(snapshot.additionalSnapshots || [])];
|
|
332
370
|
if (percy.dryRun) return allSnapshots.map(s => callback(s)); // keep a global resource cache across snapshots
|
|
333
371
|
|
|
334
|
-
let cache = percy[RESOURCE_CACHE_KEY] || (percy[RESOURCE_CACHE_KEY] = new Map()); //
|
|
335
|
-
|
|
336
|
-
let widths = snapshot.widths.slice(); // preload the root resource for existing dom snapshots
|
|
372
|
+
let cache = percy[RESOURCE_CACHE_KEY] || (percy[RESOURCE_CACHE_KEY] = new Map()); // preload the root resource for existing dom snapshots
|
|
337
373
|
|
|
338
374
|
let resources = new Map(snapshot.domSnapshot && [createRootResource(snapshot.url, snapshot.domSnapshot)].map(resource => [resource.url, resource])); // open a new browser page
|
|
339
375
|
|
|
@@ -356,33 +392,15 @@ export async function* discoverSnapshotResources(percy, snapshot, callback) {
|
|
|
356
392
|
});
|
|
357
393
|
|
|
358
394
|
try {
|
|
359
|
-
//
|
|
360
|
-
yield page.resize({
|
|
361
|
-
width: widths.shift(),
|
|
362
|
-
height: snapshot.minHeight
|
|
363
|
-
}); // navigate to the url
|
|
364
|
-
|
|
365
|
-
yield page.goto(snapshot.url);
|
|
366
|
-
|
|
367
|
-
if (snapshot.execute) {
|
|
368
|
-
// when any execute options are provided, inject snapshot options
|
|
369
|
-
|
|
370
|
-
/* istanbul ignore next: cannot detect coverage of injected code */
|
|
371
|
-
yield page.eval((_, s) => window.__PERCY__.snapshot = s, snapshot);
|
|
372
|
-
yield page.evaluate(snapshot.execute.afterNavigation);
|
|
373
|
-
} // trigger resize events for other widths
|
|
374
|
-
|
|
395
|
+
yield* triggerResourceRequests(page, snapshot); // trigger resource requests for any alternate device pixel ratio
|
|
375
396
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
yield page.evaluate((_snapshot$execute = snapshot.execute) === null || _snapshot$execute === void 0 ? void 0 : _snapshot$execute.beforeResize);
|
|
397
|
+
if (snapshot.devicePixelRatio) {
|
|
398
|
+
// wait for any existing pending resource requests first
|
|
380
399
|
yield waitForDiscoveryNetworkIdle(page, snapshot.discovery);
|
|
381
|
-
yield page
|
|
382
|
-
|
|
383
|
-
|
|
400
|
+
yield* triggerResourceRequests(page, snapshot, {
|
|
401
|
+
deviceScaleFactor: snapshot.devicePixelRatio,
|
|
402
|
+
mobile: true
|
|
384
403
|
});
|
|
385
|
-
yield page.evaluate((_snapshot$execute2 = snapshot.execute) === null || _snapshot$execute2 === void 0 ? void 0 : _snapshot$execute2.afterResize);
|
|
386
404
|
}
|
|
387
405
|
|
|
388
406
|
if (snapshot.domSnapshot) {
|
|
@@ -413,12 +431,8 @@ export async function* discoverSnapshotResources(percy, snapshot, callback) {
|
|
|
413
431
|
|
|
414
432
|
resources.delete(root.url);
|
|
415
433
|
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
await page.close();
|
|
420
|
-
} catch (error) {
|
|
434
|
+
}
|
|
435
|
+
} finally {
|
|
421
436
|
await page.close();
|
|
422
|
-
throw error;
|
|
423
437
|
}
|
|
424
438
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"test:types": "tsd"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@percy/client": "1.
|
|
43
|
-
"@percy/config": "1.
|
|
44
|
-
"@percy/dom": "1.
|
|
45
|
-
"@percy/logger": "1.
|
|
42
|
+
"@percy/client": "1.6.0",
|
|
43
|
+
"@percy/config": "1.6.0",
|
|
44
|
+
"@percy/dom": "1.6.0",
|
|
45
|
+
"@percy/logger": "1.6.0",
|
|
46
46
|
"content-disposition": "^0.5.4",
|
|
47
47
|
"cross-spawn": "^7.0.3",
|
|
48
48
|
"extract-zip": "^2.0.1",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"rimraf": "^3.0.2",
|
|
54
54
|
"ws": "^8.0.0"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "dd03aec4a68a26425cda3afd10fa142011198b5f"
|
|
57
57
|
}
|
package/test/helpers/request.js
CHANGED
|
@@ -7,7 +7,7 @@ export async function request(url, method = 'GET', handle) {
|
|
|
7
7
|
try {
|
|
8
8
|
return await request(url, options, cb);
|
|
9
9
|
} catch (error) {
|
|
10
|
-
if (typeof handle !== 'boolean') throw error;
|
|
10
|
+
if (!error.response || typeof handle !== 'boolean') throw error;
|
|
11
11
|
return handle ? [error.response.body, error.response] : error.response;
|
|
12
12
|
}
|
|
13
13
|
}
|