@percy/core 1.26.2-beta.0 → 1.26.3-alpha.4
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 +8 -7
- package/dist/browser.js +13 -5
- package/dist/discovery.js +37 -32
- package/dist/install.js +3 -2
- package/dist/network.js +78 -33
- package/dist/page.js +23 -17
- package/dist/percy.js +55 -45
- package/dist/queue.js +32 -28
- package/dist/server.js +18 -11
- package/dist/session.js +2 -1
- package/dist/snapshot.js +69 -59
- package/dist/utils.js +12 -10
- package/package.json +11 -8
package/dist/api.js
CHANGED
|
@@ -32,8 +32,8 @@ export function createPercyServer(percy, port) {
|
|
|
32
32
|
|
|
33
33
|
// skip or change api version header in testing mode
|
|
34
34
|
if (((_percy$testing = percy.testing) === null || _percy$testing === void 0 ? void 0 : _percy$testing.version) !== false) {
|
|
35
|
-
var _percy$testing2;
|
|
36
|
-
res.setHeader('X-Percy-Core-Version', ((_percy$testing2 = percy.testing) === null || _percy$testing2 === void 0 ? void 0 : _percy$testing2.version)
|
|
35
|
+
var _percy$testing$versio, _percy$testing2;
|
|
36
|
+
res.setHeader('X-Percy-Core-Version', (_percy$testing$versio = (_percy$testing2 = percy.testing) === null || _percy$testing2 === void 0 ? void 0 : _percy$testing2.version) !== null && _percy$testing$versio !== void 0 ? _percy$testing$versio : pkg.version);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
// track all api reqeusts in testing mode
|
|
@@ -58,8 +58,8 @@ export function createPercyServer(percy, port) {
|
|
|
58
58
|
|
|
59
59
|
// return json errors
|
|
60
60
|
return next().catch(e => {
|
|
61
|
-
var _percy$testing6;
|
|
62
|
-
return res.json(e.status
|
|
61
|
+
var _e$status, _percy$testing6;
|
|
62
|
+
return res.json((_e$status = e.status) !== null && _e$status !== void 0 ? _e$status : 500, {
|
|
63
63
|
build: ((_percy$testing6 = percy.testing) === null || _percy$testing6 === void 0 ? void 0 : _percy$testing6.build) || percy.build,
|
|
64
64
|
error: e.message,
|
|
65
65
|
success: false
|
|
@@ -68,9 +68,9 @@ export function createPercyServer(percy, port) {
|
|
|
68
68
|
})
|
|
69
69
|
// healthcheck returns basic information
|
|
70
70
|
.route('get', '/percy/healthcheck', (req, res) => {
|
|
71
|
-
var _percy$testing7;
|
|
71
|
+
var _percy$testing$build2, _percy$testing7;
|
|
72
72
|
return res.json(200, {
|
|
73
|
-
build: ((_percy$testing7 = percy.testing) === null || _percy$testing7 === void 0 ? void 0 : _percy$testing7.build)
|
|
73
|
+
build: (_percy$testing$build2 = (_percy$testing7 = percy.testing) === null || _percy$testing7 === void 0 ? void 0 : _percy$testing7.build) !== null && _percy$testing$build2 !== void 0 ? _percy$testing$build2 : percy.build,
|
|
74
74
|
loglevel: percy.loglevel(),
|
|
75
75
|
config: percy.config,
|
|
76
76
|
success: true
|
|
@@ -202,6 +202,7 @@ export function createPercyServer(percy, port) {
|
|
|
202
202
|
|
|
203
203
|
// Create a static server instance with an automatic sitemap
|
|
204
204
|
export function createStaticServer(options) {
|
|
205
|
+
var _options$rewrites;
|
|
205
206
|
let {
|
|
206
207
|
serve: dir,
|
|
207
208
|
baseUrl = ''
|
|
@@ -214,7 +215,7 @@ export function createStaticServer(options) {
|
|
|
214
215
|
// used when generating an automatic sitemap
|
|
215
216
|
let toURL = Server.createRewriter(
|
|
216
217
|
// reverse rewrites' src, dest, & order
|
|
217
|
-
Object.entries((options === null || options === void 0 ? void 0 : options.rewrites)
|
|
218
|
+
Object.entries((_options$rewrites = options === null || options === void 0 ? void 0 : options.rewrites) !== null && _options$rewrites !== void 0 ? _options$rewrites : {}).reduce((acc, rw) => [rw.reverse(), ...acc], []), (filename, rewrite) => new URL(path.posix.join('/', baseUrl,
|
|
218
219
|
// cleanUrls will trim trailing .html/index.html from paths
|
|
219
220
|
!options.cleanUrls ? rewrite(filename) : rewrite(filename).replace(/(\/index)?\.html$/, '')), server.address()));
|
|
220
221
|
|
package/dist/browser.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
3
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
5
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
1
6
|
import fs from 'fs';
|
|
2
7
|
import os from 'os';
|
|
3
8
|
import path from 'path';
|
|
@@ -67,6 +72,7 @@ export class Browser extends EventEmitter {
|
|
|
67
72
|
super().percy = percy;
|
|
68
73
|
}
|
|
69
74
|
async launch() {
|
|
75
|
+
var _executable;
|
|
70
76
|
// already launching or launched
|
|
71
77
|
if (this.readyState != null) return;
|
|
72
78
|
this.readyState = 0;
|
|
@@ -80,7 +86,7 @@ export class Browser extends EventEmitter {
|
|
|
80
86
|
args = [],
|
|
81
87
|
timeout
|
|
82
88
|
} = launchOptions;
|
|
83
|
-
executable
|
|
89
|
+
(_executable = executable) !== null && _executable !== void 0 ? _executable : executable = process.env.PERCY_BROWSER_EXECUTABLE;
|
|
84
90
|
|
|
85
91
|
// transform cookies object to an array of cookie params
|
|
86
92
|
this.cookies = Array.isArray(cookies) ? cookies : Object.entries(cookies).map(([name, value]) => ({
|
|
@@ -212,10 +218,9 @@ export class Browser extends EventEmitter {
|
|
|
212
218
|
let id = ++this.#lastid;
|
|
213
219
|
if (!params && typeof method === 'object') {
|
|
214
220
|
// allow providing a raw message as the only argument and return the id
|
|
215
|
-
this.ws.send(JSON.stringify({
|
|
216
|
-
...method,
|
|
221
|
+
this.ws.send(JSON.stringify(_objectSpread(_objectSpread({}, method), {}, {
|
|
217
222
|
id
|
|
218
|
-
}));
|
|
223
|
+
})));
|
|
219
224
|
return id;
|
|
220
225
|
} else {
|
|
221
226
|
// send the message payload
|
|
@@ -251,7 +256,10 @@ export class Browser extends EventEmitter {
|
|
|
251
256
|
if (match) cleanup(() => resolve(match[1]));
|
|
252
257
|
};
|
|
253
258
|
let handleExitClose = () => handleError();
|
|
254
|
-
let handleError = error => cleanup(() =>
|
|
259
|
+
let handleError = error => cleanup(() => {
|
|
260
|
+
var _error$message;
|
|
261
|
+
return reject(new Error(`Failed to launch browser. ${(_error$message = error === null || error === void 0 ? void 0 : error.message) !== null && _error$message !== void 0 ? _error$message : ''}\n${stderr}'\n\n`));
|
|
262
|
+
});
|
|
255
263
|
let cleanup = callback => {
|
|
256
264
|
clearTimeout(timeoutId);
|
|
257
265
|
this.process.stderr.off('data', handleData);
|
package/dist/discovery.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
const _excluded = ["domSnapshot", "resources"],
|
|
2
|
+
_excluded2 = ["discovery", "additionalSnapshots"],
|
|
3
|
+
_excluded3 = ["additionalSnapshots"];
|
|
4
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
5
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
6
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
7
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
8
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
9
|
+
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
10
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
1
11
|
import logger from '@percy/logger';
|
|
2
12
|
import Queue from './queue.js';
|
|
3
13
|
import { normalizeURL, hostnameMatches, createResource, createRootResource, createPercyCSSResource, createLogResource, yieldAll } from './utils.js';
|
|
@@ -95,24 +105,24 @@ function parseDomResources({
|
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
// Calls the provided callback with additional resources
|
|
98
|
-
function processSnapshotResources({
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
function processSnapshotResources(_ref) {
|
|
109
|
+
var _resources$values, _resources, _domSnapshot$html;
|
|
110
|
+
let {
|
|
111
|
+
domSnapshot,
|
|
112
|
+
resources
|
|
113
|
+
} = _ref,
|
|
114
|
+
snapshot = _objectWithoutProperties(_ref, _excluded);
|
|
115
|
+
resources = [...((_resources$values = (_resources = resources) === null || _resources === void 0 ? void 0 : _resources.values()) !== null && _resources$values !== void 0 ? _resources$values : [])];
|
|
105
116
|
|
|
106
117
|
// find any root resource matching the provided dom snapshot
|
|
107
|
-
let rootContent = (domSnapshot === null || domSnapshot === void 0 ? void 0 : domSnapshot.html)
|
|
118
|
+
let rootContent = (_domSnapshot$html = domSnapshot === null || domSnapshot === void 0 ? void 0 : domSnapshot.html) !== null && _domSnapshot$html !== void 0 ? _domSnapshot$html : domSnapshot;
|
|
108
119
|
let root = resources.find(r => r.content === rootContent);
|
|
109
120
|
|
|
110
121
|
// initialize root resources if needed
|
|
111
122
|
if (!root) {
|
|
112
|
-
let domResources = parseDomResources({
|
|
113
|
-
...snapshot,
|
|
123
|
+
let domResources = parseDomResources(_objectSpread(_objectSpread({}, snapshot), {}, {
|
|
114
124
|
domSnapshot
|
|
115
|
-
});
|
|
125
|
+
}));
|
|
116
126
|
resources = [...domResources.values(), ...resources];
|
|
117
127
|
root = resources[0];
|
|
118
128
|
}
|
|
@@ -131,20 +141,19 @@ function processSnapshotResources({
|
|
|
131
141
|
var _log$meta$snapshot;
|
|
132
142
|
return ((_log$meta$snapshot = log.meta.snapshot) === null || _log$meta$snapshot === void 0 ? void 0 : _log$meta$snapshot.name) === snapshot.meta.snapshot.name;
|
|
133
143
|
})));
|
|
134
|
-
return {
|
|
135
|
-
...snapshot,
|
|
144
|
+
return _objectSpread(_objectSpread({}, snapshot), {}, {
|
|
136
145
|
resources
|
|
137
|
-
};
|
|
146
|
+
});
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
// Triggers the capture of resource requests for a page by iterating over snapshot widths to resize
|
|
141
150
|
// the page and calling any provided execute options.
|
|
142
151
|
async function* captureSnapshotResources(page, snapshot, options) {
|
|
143
152
|
let {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
discovery,
|
|
154
|
+
additionalSnapshots = []
|
|
155
|
+
} = snapshot,
|
|
156
|
+
baseSnapshot = _objectWithoutProperties(snapshot, _excluded2);
|
|
148
157
|
let {
|
|
149
158
|
capture,
|
|
150
159
|
captureWidths,
|
|
@@ -154,10 +163,9 @@ async function* captureSnapshotResources(page, snapshot, options) {
|
|
|
154
163
|
|
|
155
164
|
// used to take snapshots and remove any discovered root resource
|
|
156
165
|
let takeSnapshot = async (options, width) => {
|
|
157
|
-
if (captureWidths) options = {
|
|
158
|
-
...options,
|
|
166
|
+
if (captureWidths) options = _objectSpread(_objectSpread({}, options), {}, {
|
|
159
167
|
width
|
|
160
|
-
};
|
|
168
|
+
});
|
|
161
169
|
let captured = await page.snapshot(options);
|
|
162
170
|
captured.resources.delete(normalizeURL(captured.url));
|
|
163
171
|
capture(processSnapshotResources(captured));
|
|
@@ -185,10 +193,7 @@ async function* captureSnapshotResources(page, snapshot, options) {
|
|
|
185
193
|
// iterate over additional snapshots for proper DOM capturing
|
|
186
194
|
for (let additionalSnapshot of [baseSnapshot, ...additionalSnapshots]) {
|
|
187
195
|
let isBaseSnapshot = additionalSnapshot === baseSnapshot;
|
|
188
|
-
let snap = {
|
|
189
|
-
...baseSnapshot,
|
|
190
|
-
...additionalSnapshot
|
|
191
|
-
};
|
|
196
|
+
let snap = _objectSpread(_objectSpread({}, baseSnapshot), additionalSnapshot);
|
|
192
197
|
let {
|
|
193
198
|
widths,
|
|
194
199
|
execute
|
|
@@ -247,9 +252,9 @@ export async function* discoverSnapshotResources(queue, options, callback) {
|
|
|
247
252
|
debugSnapshotOptions(snapshot);
|
|
248
253
|
if (skipDiscovery) {
|
|
249
254
|
let {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
255
|
+
additionalSnapshots
|
|
256
|
+
} = snapshot,
|
|
257
|
+
baseSnapshot = _objectWithoutProperties(snapshot, _excluded3);
|
|
253
258
|
additionalSnapshots = dryRun && additionalSnapshots || [];
|
|
254
259
|
for (let snap of [baseSnapshot, ...additionalSnapshots]) {
|
|
255
260
|
callback(dryRun ? snap : processSnapshotResources(snap));
|
|
@@ -293,18 +298,18 @@ export function createDiscoveryQueue(percy) {
|
|
|
293
298
|
// initialize the resources for DOM snapshots
|
|
294
299
|
.handle('push', snapshot => {
|
|
295
300
|
let resources = parseDomResources(snapshot);
|
|
296
|
-
return {
|
|
297
|
-
...snapshot,
|
|
301
|
+
return _objectSpread(_objectSpread({}, snapshot), {}, {
|
|
298
302
|
resources
|
|
299
|
-
};
|
|
303
|
+
});
|
|
300
304
|
})
|
|
301
305
|
// discovery resources for snapshots and call the callback for each discovered snapshot
|
|
302
306
|
.handle('task', async function* (snapshot, callback) {
|
|
307
|
+
var _snapshot$enableJavaS;
|
|
303
308
|
percy.log.debug(`Discovering resources: ${snapshot.name}`, snapshot.meta);
|
|
304
309
|
|
|
305
310
|
// expectation explained in tests
|
|
306
311
|
/* istanbul ignore next: tested, but coverage is stripped */
|
|
307
|
-
let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || (snapshot.enableJavaScript
|
|
312
|
+
let assetDiscoveryPageEnableJS = snapshot.cliEnableJavaScript && !snapshot.domSnapshot || ((_snapshot$enableJavaS = snapshot.enableJavaScript) !== null && _snapshot$enableJavaS !== void 0 ? _snapshot$enableJavaS : !snapshot.domSnapshot);
|
|
308
313
|
percy.log.debug(`Asset discovery Browser Page enable JS: ${assetDiscoveryPageEnableJS}`);
|
|
309
314
|
// create a new browser page
|
|
310
315
|
let page = yield percy.browser.page({
|
package/dist/install.js
CHANGED
|
@@ -84,8 +84,9 @@ export async function download({
|
|
|
84
84
|
let total = parseInt(response.headers['content-length'], 10);
|
|
85
85
|
let start, progress;
|
|
86
86
|
response.on('data', chunk => {
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
var _start, _progress;
|
|
88
|
+
(_start = start) !== null && _start !== void 0 ? _start : start = Date.now();
|
|
89
|
+
progress = ((_progress = progress) !== null && _progress !== void 0 ? _progress : 0) + chunk.length;
|
|
89
90
|
log.progress(formatProgress(premsg, total, start, progress));
|
|
90
91
|
});
|
|
91
92
|
}
|
package/dist/network.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
2
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
3
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
4
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
5
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
1
6
|
import mime from 'mime-types';
|
|
2
7
|
import logger from '@percy/logger';
|
|
3
8
|
import { request as makeRequest } from '@percy/client/utils';
|
|
@@ -5,6 +10,7 @@ import { normalizeURL, hostnameMatches, createResource, waitFor } from './utils.
|
|
|
5
10
|
const MAX_RESOURCE_SIZE = 25 * 1024 ** 2; // 25MB
|
|
6
11
|
const ALLOWED_STATUSES = [200, 201, 301, 302, 304, 307, 308];
|
|
7
12
|
const ALLOWED_RESOURCES = ['Document', 'Stylesheet', 'Image', 'Media', 'Font', 'Other'];
|
|
13
|
+
const ABORTED_MESSAGE = 'Request was aborted by browser';
|
|
8
14
|
|
|
9
15
|
// The Interceptor class creates common handlers for dealing with intercepting asset requests
|
|
10
16
|
// for a given page using various devtools protocol events and commands.
|
|
@@ -15,12 +21,14 @@ export class Network {
|
|
|
15
21
|
#requests = new Map();
|
|
16
22
|
#intercepts = new Map();
|
|
17
23
|
#authentications = new Set();
|
|
24
|
+
#aborted = new Set();
|
|
18
25
|
constructor(page, options) {
|
|
26
|
+
var _options$networkIdleT, _options$requestHeade, _options$userAgent;
|
|
19
27
|
this.page = page;
|
|
20
|
-
this.timeout = options.networkIdleTimeout
|
|
28
|
+
this.timeout = (_options$networkIdleT = options.networkIdleTimeout) !== null && _options$networkIdleT !== void 0 ? _options$networkIdleT : 100;
|
|
21
29
|
this.authorization = options.authorization;
|
|
22
|
-
this.requestHeaders = options.requestHeaders
|
|
23
|
-
this.userAgent = options.userAgent
|
|
30
|
+
this.requestHeaders = (_options$requestHeade = options.requestHeaders) !== null && _options$requestHeade !== void 0 ? _options$requestHeade : {};
|
|
31
|
+
this.userAgent = (_options$userAgent = options.userAgent) !== null && _options$userAgent !== void 0 ? _options$userAgent :
|
|
24
32
|
// by default, emulate a non-headless browser
|
|
25
33
|
page.session.browser.version.userAgent.replace('Headless', '');
|
|
26
34
|
this.intercept = options.intercept;
|
|
@@ -77,6 +85,23 @@ export class Network {
|
|
|
77
85
|
});
|
|
78
86
|
}
|
|
79
87
|
|
|
88
|
+
// Validates that requestId is still valid as sometimes request gets cancelled and we have already executed
|
|
89
|
+
// _forgetRequest for the same, but we still attempt to make a call for it and it fails
|
|
90
|
+
// with Protocol error (Fetch.failRequest): Invalid InterceptionId.
|
|
91
|
+
async send(session, method, params) {
|
|
92
|
+
/* istanbul ignore else: currently all send have requestId */
|
|
93
|
+
if (params.requestId) {
|
|
94
|
+
/* istanbul ignore if: race condition, very hard to mock this */
|
|
95
|
+
if (this.isAborted(params.requestId)) {
|
|
96
|
+
throw new Error(ABORTED_MESSAGE);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return await session.send(method, params);
|
|
100
|
+
}
|
|
101
|
+
isAborted(requestId) {
|
|
102
|
+
return this.#aborted.has(requestId);
|
|
103
|
+
}
|
|
104
|
+
|
|
80
105
|
// Throw a better network timeout error
|
|
81
106
|
_throwTimeoutError(msg, filter = () => true) {
|
|
82
107
|
if (this.log.shouldLog('debug')) {
|
|
@@ -102,10 +127,11 @@ export class Network {
|
|
|
102
127
|
// Called when a request requires authentication. Responds to the auth request with any
|
|
103
128
|
// provided authorization credentials.
|
|
104
129
|
_handleAuthRequired = async (session, event) => {
|
|
130
|
+
var _this$authorization;
|
|
105
131
|
let {
|
|
106
132
|
username,
|
|
107
133
|
password
|
|
108
|
-
} = this.authorization
|
|
134
|
+
} = (_this$authorization = this.authorization) !== null && _this$authorization !== void 0 ? _this$authorization : {};
|
|
109
135
|
let {
|
|
110
136
|
requestId
|
|
111
137
|
} = event;
|
|
@@ -116,7 +142,7 @@ export class Network {
|
|
|
116
142
|
response = 'ProvideCredentials';
|
|
117
143
|
this.#authentications.add(requestId);
|
|
118
144
|
}
|
|
119
|
-
await
|
|
145
|
+
await this.send(session, 'Fetch.continueWithAuth', {
|
|
120
146
|
requestId: event.requestId,
|
|
121
147
|
authChallengeResponse: {
|
|
122
148
|
response,
|
|
@@ -139,17 +165,15 @@ export class Network {
|
|
|
139
165
|
|
|
140
166
|
// guard against redirects with the same requestId
|
|
141
167
|
if ((pending === null || pending === void 0 ? void 0 : pending.request.url) === event.request.url && pending.request.method === event.request.method) {
|
|
142
|
-
await this._handleRequest(session, {
|
|
143
|
-
...pending,
|
|
168
|
+
await this._handleRequest(session, _objectSpread(_objectSpread({}, pending), {}, {
|
|
144
169
|
resourceType,
|
|
145
170
|
interceptId
|
|
146
|
-
});
|
|
171
|
+
}));
|
|
147
172
|
} else {
|
|
148
173
|
// track the session that intercepted the request
|
|
149
|
-
this.#intercepts.set(requestId, {
|
|
150
|
-
...event,
|
|
174
|
+
this.#intercepts.set(requestId, _objectSpread(_objectSpread({}, event), {}, {
|
|
151
175
|
session
|
|
152
|
-
});
|
|
176
|
+
}));
|
|
153
177
|
}
|
|
154
178
|
};
|
|
155
179
|
|
|
@@ -173,11 +197,10 @@ export class Network {
|
|
|
173
197
|
requestId: interceptId,
|
|
174
198
|
resourceType
|
|
175
199
|
} = intercept;
|
|
176
|
-
await this._handleRequest(session, {
|
|
177
|
-
...event,
|
|
200
|
+
await this._handleRequest(session, _objectSpread(_objectSpread({}, event), {}, {
|
|
178
201
|
resourceType,
|
|
179
202
|
interceptId
|
|
180
|
-
});
|
|
203
|
+
}));
|
|
181
204
|
this.#intercepts.delete(requestId);
|
|
182
205
|
}
|
|
183
206
|
}
|
|
@@ -222,7 +245,7 @@ export class Network {
|
|
|
222
245
|
if (!request) return;
|
|
223
246
|
request.response = response;
|
|
224
247
|
request.response.buffer = async () => {
|
|
225
|
-
let result = await
|
|
248
|
+
let result = await this.send(session, 'Network.getResponseBody', {
|
|
226
249
|
requestId
|
|
227
250
|
});
|
|
228
251
|
return Buffer.from(result.body, result.base64Encoded ? 'base64' : 'utf-8');
|
|
@@ -253,13 +276,21 @@ export class Network {
|
|
|
253
276
|
/* istanbul ignore if: race condition paranioa */
|
|
254
277
|
if (!request) return;
|
|
255
278
|
|
|
256
|
-
//
|
|
257
|
-
|
|
279
|
+
// If request was aborted, keep track of it as we need to cancel any in process callbacks for
|
|
280
|
+
// such a request to avoid Invalid InterceptionId errors
|
|
281
|
+
// Note: 404s also show up under ERR_ABORTED and not ERR_FAILED
|
|
282
|
+
if (event.errorText === 'net::ERR_ABORTED') {
|
|
283
|
+
let message = `Request aborted for ${request.url}: ${event.errorText}`;
|
|
284
|
+
this.log.debug(message, _objectSpread(_objectSpread({}, this.meta), {}, {
|
|
285
|
+
url: request.url
|
|
286
|
+
}));
|
|
287
|
+
this.#aborted.add(request.requestId);
|
|
288
|
+
} else if (event.errorText !== 'net::ERR_FAILED') {
|
|
289
|
+
// do not log generic messages since the real error was likely logged elsewhere
|
|
258
290
|
let message = `Request failed for ${request.url}: ${event.errorText}`;
|
|
259
|
-
this.log.debug(message, {
|
|
260
|
-
...this.meta,
|
|
291
|
+
this.log.debug(message, _objectSpread(_objectSpread({}, this.meta), {}, {
|
|
261
292
|
url: request.url
|
|
262
|
-
});
|
|
293
|
+
}));
|
|
263
294
|
}
|
|
264
295
|
this._forgetRequest(request);
|
|
265
296
|
};
|
|
@@ -285,22 +316,22 @@ async function sendResponseResource(network, request, session) {
|
|
|
285
316
|
} = network.intercept;
|
|
286
317
|
let log = network.log;
|
|
287
318
|
let url = originURL(request);
|
|
288
|
-
let meta = {
|
|
289
|
-
...network.meta,
|
|
319
|
+
let meta = _objectSpread(_objectSpread({}, network.meta), {}, {
|
|
290
320
|
url
|
|
291
|
-
};
|
|
321
|
+
});
|
|
322
|
+
let send = (method, params) => network.send(session, method, params);
|
|
292
323
|
try {
|
|
293
324
|
let resource = network.intercept.getResource(url);
|
|
294
325
|
network.log.debug(`Handling request: ${url}`, meta);
|
|
295
326
|
if (!(resource !== null && resource !== void 0 && resource.root) && hostnameMatches(disallowedHostnames, url)) {
|
|
296
327
|
log.debug('- Skipping disallowed hostname', meta);
|
|
297
|
-
await
|
|
328
|
+
await send('Fetch.failRequest', {
|
|
298
329
|
requestId: request.interceptId,
|
|
299
330
|
errorReason: 'Aborted'
|
|
300
331
|
});
|
|
301
332
|
} else if (resource && (resource.root || resource.provided || !disableCache)) {
|
|
302
333
|
log.debug(resource.root ? '- Serving root resource' : '- Resource cache hit', meta);
|
|
303
|
-
await
|
|
334
|
+
await send('Fetch.fulfillRequest', {
|
|
304
335
|
requestId: request.interceptId,
|
|
305
336
|
responseCode: resource.status || 200,
|
|
306
337
|
body: Buffer.from(resource.content).toString('base64'),
|
|
@@ -310,18 +341,35 @@ async function sendResponseResource(network, request, session) {
|
|
|
310
341
|
}))
|
|
311
342
|
});
|
|
312
343
|
} else {
|
|
313
|
-
await
|
|
344
|
+
await send('Fetch.continueRequest', {
|
|
314
345
|
requestId: request.interceptId
|
|
315
346
|
});
|
|
316
347
|
}
|
|
317
348
|
} catch (error) {
|
|
318
349
|
/* istanbul ignore next: too hard to test (create race condition) */
|
|
319
350
|
if (session.closing && error.message.includes('close')) return;
|
|
351
|
+
|
|
352
|
+
// if failure is due to an already aborted request, ignore it
|
|
353
|
+
// due to race condition we might get aborted event later and see a `Invalid InterceptionId`
|
|
354
|
+
// error before, in which case we should wait for a tick and check again
|
|
355
|
+
// Note: its not a necessity that we would get aborted callback in a tick, its just that if we
|
|
356
|
+
// already have it then we can safely ignore this error
|
|
357
|
+
// Its very hard to test it as this function should be called and request should get cancelled before
|
|
358
|
+
if (error.message === ABORTED_MESSAGE || error.message.includes('Invalid InterceptionId')) {
|
|
359
|
+
// defer this to the end of queue to make sure that any incoming aborted messages were
|
|
360
|
+
// handled and network.#aborted is updated
|
|
361
|
+
await new Promise((res, _) => process.nextTick(res));
|
|
362
|
+
/* istanbul ignore else: too hard to create race where abortion event is delayed */
|
|
363
|
+
if (network.isAborted(request.requestId)) {
|
|
364
|
+
log.debug(`Ignoring further steps for ${url} as request was aborted by the browser.`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
320
368
|
log.debug(`Encountered an error handling request: ${url}`, meta);
|
|
321
369
|
log.debug(error);
|
|
322
370
|
|
|
323
371
|
/* istanbul ignore next: catch race condition */
|
|
324
|
-
await
|
|
372
|
+
await send('Fetch.failRequest', {
|
|
325
373
|
requestId: request.interceptId,
|
|
326
374
|
errorReason: 'Failed'
|
|
327
375
|
}).catch(e => log.debug(e, meta));
|
|
@@ -331,9 +379,7 @@ async function sendResponseResource(network, request, session) {
|
|
|
331
379
|
// Make a new request with Node based on a network request
|
|
332
380
|
function makeDirectRequest(network, request) {
|
|
333
381
|
var _network$authorizatio;
|
|
334
|
-
let headers = {
|
|
335
|
-
...request.headers
|
|
336
|
-
};
|
|
382
|
+
let headers = _objectSpread({}, request.headers);
|
|
337
383
|
if ((_network$authorizatio = network.authorization) !== null && _network$authorizatio !== void 0 && _network$authorizatio.username) {
|
|
338
384
|
// include basic authorization username and password
|
|
339
385
|
let {
|
|
@@ -359,10 +405,9 @@ async function saveResponseResource(network, request) {
|
|
|
359
405
|
let log = network.log;
|
|
360
406
|
let url = originURL(request);
|
|
361
407
|
let response = request.response;
|
|
362
|
-
let meta = {
|
|
363
|
-
...network.meta,
|
|
408
|
+
let meta = _objectSpread(_objectSpread({}, network.meta), {}, {
|
|
364
409
|
url
|
|
365
|
-
};
|
|
410
|
+
});
|
|
366
411
|
let resource = network.intercept.getResource(url);
|
|
367
412
|
if (!resource || !resource.root && !resource.provided && disableCache) {
|
|
368
413
|
try {
|
package/dist/page.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
const _excluded = ["waitForTimeout", "waitForSelector", "execute"];
|
|
2
|
+
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
3
|
+
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
4
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
5
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
6
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
7
|
+
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
8
|
+
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
9
|
+
function _objectDestructuringEmpty(obj) { if (obj == null) throw new TypeError("Cannot destructure " + obj); }
|
|
1
10
|
import fs from 'fs';
|
|
2
11
|
import logger from '@percy/logger';
|
|
3
12
|
import Network from './network.js';
|
|
@@ -7,9 +16,10 @@ export class Page {
|
|
|
7
16
|
static TIMEOUT = undefined;
|
|
8
17
|
log = logger('core:page');
|
|
9
18
|
constructor(session, options) {
|
|
19
|
+
var _options$enableJavaSc;
|
|
10
20
|
this.session = session;
|
|
11
21
|
this.browser = session.browser;
|
|
12
|
-
this.enableJavaScript = options.enableJavaScript
|
|
22
|
+
this.enableJavaScript = (_options$enableJavaSc = options.enableJavaScript) !== null && _options$enableJavaSc !== void 0 ? _options$enableJavaSc : true;
|
|
13
23
|
this.network = new Network(this, options);
|
|
14
24
|
this.meta = options.meta;
|
|
15
25
|
this._initializeLoadTimeout();
|
|
@@ -53,9 +63,8 @@ export class Page {
|
|
|
53
63
|
let defaultDomain = hostname(url);
|
|
54
64
|
await this.session.send('Network.setCookies', {
|
|
55
65
|
// spread is used to make a shallow copy of the cookie
|
|
56
|
-
cookies: this.session.browser.cookies.map(
|
|
57
|
-
|
|
58
|
-
}) => {
|
|
66
|
+
cookies: this.session.browser.cookies.map(_ref => {
|
|
67
|
+
let cookie = Object.assign({}, (_objectDestructuringEmpty(_ref), _ref));
|
|
59
68
|
if (!cookie.url) cookie.domain || (cookie.domain = defaultDomain);
|
|
60
69
|
return cookie;
|
|
61
70
|
})
|
|
@@ -125,22 +134,22 @@ export class Page {
|
|
|
125
134
|
async evaluate(scripts) {
|
|
126
135
|
var _scripts;
|
|
127
136
|
if (!((_scripts = scripts && (scripts = [].concat(scripts))) !== null && _scripts !== void 0 && _scripts.length)) return;
|
|
128
|
-
this.log.debug('Evaluate JavaScript', {
|
|
129
|
-
...this.meta,
|
|
137
|
+
this.log.debug('Evaluate JavaScript', _objectSpread(_objectSpread({}, this.meta), {}, {
|
|
130
138
|
scripts
|
|
131
|
-
});
|
|
139
|
+
}));
|
|
132
140
|
for (let script of scripts) await this.eval(script);
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
// Takes a snapshot after waiting for any timeout, waiting for any selector, executing any
|
|
136
144
|
// scripts, and waiting for the network idle. Returns all other provided snapshot options along
|
|
137
145
|
// with the captured URL and DOM snapshot.
|
|
138
|
-
async snapshot({
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
async snapshot(_ref2) {
|
|
147
|
+
let {
|
|
148
|
+
waitForTimeout,
|
|
149
|
+
waitForSelector,
|
|
150
|
+
execute
|
|
151
|
+
} = _ref2,
|
|
152
|
+
snapshot = _objectWithoutProperties(_ref2, _excluded);
|
|
144
153
|
let {
|
|
145
154
|
name,
|
|
146
155
|
width,
|
|
@@ -193,10 +202,7 @@ export class Page {
|
|
|
193
202
|
disableShadowDOM,
|
|
194
203
|
domTransformation
|
|
195
204
|
});
|
|
196
|
-
return {
|
|
197
|
-
...snapshot,
|
|
198
|
-
...capture
|
|
199
|
-
};
|
|
205
|
+
return _objectSpread(_objectSpread({}, snapshot), capture);
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
// Initialize newly attached pages and iframes with page options
|