@percy/core
Advanced tools
Comparing version 1.0.0-beta.63 to 1.0.0-beta.64
@@ -72,3 +72,3 @@ "use strict"; | ||
_defineProperty(this, "defaultArgs", ['--enable-features=NetworkService,NetworkServiceInProcess', '--disable-background-networking', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-breakpad', '--disable-client-side-phishing-detection', '--disable-component-extensions-with-background-pages', '--disable-default-apps', '--disable-dev-shm-usage', '--disable-extensions', '--disable-features=TranslateUI', '--disable-hang-monitor', '--disable-ipc-flooding-protection', '--disable-popup-blocking', '--disable-prompt-on-repost', '--disable-renderer-backgrounding', '--disable-sync', '--disable-web-security', '--force-color-profile=srgb', '--metrics-recording-only', '--no-first-run', '--no-sandbox', '--enable-automation', '--password-store=basic', '--use-mock-keychain', '--remote-debugging-port=0']); | ||
_defineProperty(this, "defaultArgs", ['--enable-features=NetworkService,NetworkServiceInProcess', '--disable-features=Translate', '--disable-background-networking', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-breakpad', '--disable-client-side-phishing-detection', '--disable-component-extensions-with-background-pages', '--disable-default-apps', '--disable-dev-shm-usage', '--disable-extensions', '--disable-hang-monitor', '--disable-ipc-flooding-protection', '--disable-popup-blocking', '--disable-prompt-on-repost', '--disable-renderer-backgrounding', '--disable-sync', '--disable-web-security', '--force-color-profile=srgb', '--metrics-recording-only', '--no-first-run', '--no-sandbox', '--enable-automation', '--password-store=basic', '--use-mock-keychain', '--remote-debugging-port=0']); | ||
@@ -75,0 +75,0 @@ this.launchTimeout = timeout; |
@@ -54,3 +54,5 @@ "use strict"; | ||
} = process; | ||
return map[platform === 'win32' && arch === 'x64' ? 'win64' : platform]; | ||
if (platform === 'win32' && arch === 'x64') platform = 'win64'; | ||
if (platform === 'darwin' && arch === 'arm64') platform = 'darwinArm'; | ||
return map[platform]; | ||
} // Installs a revision of Chromium to a local directory | ||
@@ -72,2 +74,3 @@ | ||
darwin: `Mac/${revision}/chrome-mac.zip`, | ||
darwinArm: `Mac_Arm/${revision}/chrome-mac.zip`, | ||
win64: `Win_x64/${revision}/chrome-win.zip`, | ||
@@ -80,3 +83,4 @@ win32: `Win/${revision}/chrome-win.zip` | ||
win32: _path.default.join('chrome-win', 'chrome.exe'), | ||
darwin: _path.default.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium') | ||
darwin: _path.default.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'), | ||
darwinArm: _path.default.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium') | ||
}); | ||
@@ -98,3 +102,4 @@ return install({ | ||
win32: '885263', | ||
darwin: '885263' | ||
darwin: '885263', | ||
darwinArm: '885282' | ||
}; // Installs an executable from a url to a local directory, returning the full path to the extracted | ||
@@ -101,0 +106,0 @@ // binary. Skips installation if the executable already exists at the binary path. |
@@ -24,2 +24,5 @@ "use strict"; | ||
const NETWORK_TIMEOUT = 30000; // The Interceptor class creates common handlers for dealing with intercepting asset requests | ||
// for a given page using various devtools protocol events and commands. | ||
var _pending = /*#__PURE__*/new WeakMap(); | ||
@@ -33,4 +36,4 @@ | ||
// The Interceptor class creates common handlers for dealing with intercepting asset requests | ||
// for a given page using various devtools protocol events and commands. | ||
var _frames = /*#__PURE__*/new WeakMap(); | ||
class Network { | ||
@@ -58,2 +61,7 @@ constructor(page) { | ||
_frames.set(this, { | ||
writable: true, | ||
value: new Map() | ||
}); | ||
_defineProperty(this, "log", (0, _logger.default)('core:network')); | ||
@@ -96,11 +104,9 @@ | ||
let pending = _classPrivateFieldGet(this, _pending).get(requestId); // guard against redirects with the same requestId | ||
let pending = _classPrivateFieldGet(this, _pending).get(requestId); | ||
_classPrivateFieldGet(this, _pending).delete(requestId); // guard against redirects with the same requestId | ||
if ((pending === null || pending === void 0 ? void 0 : pending.request.url) === event.request.url && pending.request.method === event.request.method) { | ||
this._handleRequest(pending, event.requestId); | ||
} | ||
if (pending) { | ||
_classPrivateFieldGet(this, _pending).delete(requestId); | ||
} else { | ||
@@ -136,2 +142,3 @@ _classPrivateFieldGet(this, _intercepts).set(requestId, event); | ||
let { | ||
frameId, | ||
requestId, | ||
@@ -145,3 +152,2 @@ request | ||
req.response = event.redirectResponse; | ||
redirectChain = [...req.redirectChain, req]; // clean up interim requests | ||
@@ -152,2 +158,3 @@ | ||
request.frameId = frameId; | ||
request.requestId = requestId; | ||
@@ -216,2 +223,6 @@ request.interceptId = interceptId; | ||
}; | ||
if (request.frameId !== this.page.frameId) { | ||
_classPrivateFieldGet(this, _frames).set(request.frameId, request); | ||
} | ||
}); | ||
@@ -228,10 +239,6 @@ | ||
_defineProperty(this, "_handleLoadingFinished", async event => { | ||
let { | ||
requestId | ||
} = event; | ||
let request = _classPrivateFieldGet(this, _requests).get(event.requestId); | ||
/* istanbul ignore if: race condition paranioa */ | ||
let request = _classPrivateFieldGet(this, _requests).get(requestId); | ||
/* istanbul ignore next: race condition paranioa */ | ||
if (!request) return; | ||
@@ -247,8 +254,3 @@ | ||
_defineProperty(this, "_handleLoadingFailed", async event => { | ||
let { | ||
requestId, | ||
errorText | ||
} = event; | ||
let request = _classPrivateFieldGet(this, _requests).get(requestId); | ||
let request = _classPrivateFieldGet(this, _requests).get(event.requestId); | ||
/* istanbul ignore if: race condition paranioa */ | ||
@@ -260,3 +262,3 @@ | ||
if (this._intercept) { | ||
request.error = errorText; | ||
request.error = event.errorText; | ||
await this.onrequestfailed(request); | ||
@@ -268,2 +270,17 @@ } | ||
_defineProperty(this, "_handleFrameDetached", async event => { | ||
let request = _classPrivateFieldGet(this, _frames).get(event.frameId); | ||
/* istanbul ignore if: race condition paranioa */ | ||
if (!request) return; | ||
/* istanbul ignore else: could be false when a page is used without asset discovery */ | ||
if (this._intercept) { | ||
await this.onrequestfinished(request); | ||
} | ||
this._forgetRequest(request); | ||
}); | ||
this.page = page; | ||
@@ -277,2 +294,3 @@ this.page.on('Fetch.authRequired', this._handleAuthRequired); | ||
this.page.on('Network.loadingFailed', this._handleLoadingFailed); | ||
this.page.on('Page.frameDetached', this._handleFrameDetached); | ||
/* istanbul ignore next: race condition */ | ||
@@ -299,14 +317,31 @@ | ||
async idle(filter = r => r, timeout = this.timeout || 100) { | ||
let getRequests = () => Array.from(_classPrivateFieldGet(this, _requests).values()).reduce((a, r) => filter(r) ? a.concat(r.url) : a, []); | ||
this.log.debug(`Wait for ${timeout}ms idle`, this.page.meta); | ||
await (0, _utils.waitFor)(() => { | ||
if (this.page.closedReason) { | ||
throw new Error(`Network error: ${this.page.closedReason}`); | ||
try { | ||
await (0, _utils.waitFor)(() => { | ||
if (this.page.closedReason) { | ||
throw new Error(`Network error: ${this.page.closedReason}`); | ||
} | ||
return getRequests().length === 0; | ||
}, { | ||
timeout: NETWORK_TIMEOUT, | ||
idle: timeout | ||
}); | ||
} catch (error) { | ||
// throw a better timeout error | ||
if (error.message.startsWith('Timeout')) { | ||
let msg = 'Timed out waiting for network requests to idle.'; | ||
if (this.log.shouldLog('debug')) { | ||
msg += `\n\n ${['Active requests:', ...getRequests()].join('\n -> ')}\n`; | ||
} | ||
throw new Error(msg); | ||
} else { | ||
throw error; | ||
} | ||
return Array.from(_classPrivateFieldGet(this, _requests).values()).filter(filter).length === 0; | ||
}, { | ||
timeout: 30 * 1000, | ||
// 30 second error timeout | ||
idle: timeout | ||
}); | ||
} | ||
} // Called when a request should be removed from various trackers | ||
@@ -317,3 +352,4 @@ | ||
requestId, | ||
interceptId | ||
interceptId, | ||
frameId | ||
}, keepPending) { | ||
@@ -324,2 +360,4 @@ _classPrivateFieldGet(this, _requests).delete(requestId); | ||
_classPrivateFieldGet(this, _frames).delete(frameId); | ||
if (!keepPending) { | ||
@@ -326,0 +364,0 @@ _classPrivateFieldGet(this, _pending).delete(requestId); |
206
dist/page.js
@@ -24,9 +24,5 @@ "use strict"; | ||
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } | ||
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; } | ||
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); } | ||
function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } } | ||
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } | ||
@@ -36,57 +32,26 @@ // Used by some methods to impose a strict maximum timeout, such as .goto and .snapshot | ||
var _browser = /*#__PURE__*/new WeakMap(); | ||
var _sessionId = /*#__PURE__*/new WeakMap(); | ||
var _targetId = /*#__PURE__*/new WeakMap(); | ||
var _frameId = /*#__PURE__*/new WeakMap(); | ||
var _contextId = /*#__PURE__*/new WeakMap(); | ||
var _callbacks = /*#__PURE__*/new WeakMap(); | ||
var _lifecycle = /*#__PURE__*/new WeakMap(); | ||
class Page extends _events.default { | ||
constructor(browser, { | ||
params | ||
params, | ||
sessionId: parentId | ||
}) { | ||
super(); | ||
_browser.set(this, { | ||
_callbacks.set(this, { | ||
writable: true, | ||
value: null | ||
value: new Map() | ||
}); | ||
_sessionId.set(this, { | ||
writable: true, | ||
value: null | ||
}); | ||
_defineProperty(this, "browser", null); | ||
_targetId.set(this, { | ||
writable: true, | ||
value: null | ||
}); | ||
_defineProperty(this, "sessionId", null); | ||
_frameId.set(this, { | ||
writable: true, | ||
value: null | ||
}); | ||
_defineProperty(this, "targetId", null); | ||
_contextId.set(this, { | ||
writable: true, | ||
value: null | ||
}); | ||
_defineProperty(this, "frameId", null); | ||
_callbacks.set(this, { | ||
writable: true, | ||
value: new Map() | ||
}); | ||
_defineProperty(this, "contextId", null); | ||
_lifecycle.set(this, { | ||
writable: true, | ||
value: new Set() | ||
}); | ||
_defineProperty(this, "closedReason", null); | ||
@@ -96,13 +61,5 @@ | ||
_defineProperty(this, "_handleLifecycleEvent", event => { | ||
if (_classPrivateFieldGet(this, _frameId) === event.frameId) { | ||
if (event.name === 'init') _classPrivateFieldGet(this, _lifecycle).clear(); | ||
_classPrivateFieldGet(this, _lifecycle).add(event.name); | ||
} | ||
}); | ||
_defineProperty(this, "_handleExecutionContextCreated", event => { | ||
if (_classPrivateFieldGet(this, _frameId) === event.context.auxData.frameId) { | ||
_classPrivateFieldSet(this, _contextId, event.context.id); | ||
if (this.frameId === event.context.auxData.frameId) { | ||
this.contextId = event.context.id; | ||
} | ||
@@ -113,4 +70,4 @@ }); | ||
/* istanbul ignore next: context cleared is usually called first */ | ||
if (_classPrivateFieldGet(this, _contextId) === event.executionContextId) { | ||
_classPrivateFieldSet(this, _contextId, null); | ||
if (this.contextId === event.executionContextId) { | ||
this.contextId = null; | ||
} | ||
@@ -120,3 +77,3 @@ }); | ||
_defineProperty(this, "_handleExecutionContextsCleared", () => { | ||
_classPrivateFieldSet(this, _contextId, null); | ||
this.contextId = null; | ||
}); | ||
@@ -129,29 +86,28 @@ | ||
_classPrivateFieldSet(this, _browser, browser); | ||
_classPrivateFieldSet(this, _sessionId, params.sessionId); | ||
_classPrivateFieldSet(this, _targetId, params.targetInfo.targetId); | ||
this.browser = browser; | ||
this.sessionId = params.sessionId; | ||
this.targetId = params.targetInfo.targetId; | ||
this.network = new _network.default(this); | ||
this.on('Page.lifecycleEvent', this._handleLifecycleEvent); | ||
this.on('Runtime.executionContextCreated', this._handleExecutionContextCreated); | ||
this.on('Runtime.executionContextDestroyed', this._handleExecutionContextDestroyed); | ||
this.on('Runtime.executionContextsCleared', this._handleExecutionContextsCleared); | ||
this.on('Inspector.targetCrashed', this._handleTargetCrashed); | ||
this.on('Inspector.targetCrashed', this._handleTargetCrashed); // if there is a parent session, automatically init this session | ||
this.parent = browser.pages.get(parentId); | ||
if (this.parent) this.init(this.parent.options); | ||
} // initial page options asynchronously | ||
async init({ | ||
cacheDisabled = true, | ||
enableJavaScript = true, | ||
requestHeaders = {}, | ||
networkIdleTimeout, | ||
authorization, | ||
userAgent, | ||
intercept, | ||
height, | ||
width, | ||
meta | ||
} = {}) { | ||
async init(options = {}) { | ||
this.options = options; | ||
let { | ||
cacheDisabled = true, | ||
enableJavaScript = true, | ||
requestHeaders = {}, | ||
networkIdleTimeout, | ||
authorization, | ||
userAgent, | ||
intercept, | ||
meta | ||
} = options; | ||
this.log.debug('Initialize page', meta); | ||
@@ -164,8 +120,12 @@ this.network.timeout = networkIdleTimeout; | ||
}, version] = await Promise.all([this.send('Page.enable'), this.send('Page.getFrameTree'), this.send('Browser.getVersion')]); | ||
this.frameId = frameTree.frame.id; // by default, emulate a non-headless browser | ||
_classPrivateFieldSet(this, _frameId, frameTree.frame.id); // by default, emulate a non-headless browser | ||
userAgent || (userAgent = version.userAgent.replace('Headless', '')); // auto-attach related targets | ||
userAgent || (userAgent = version.userAgent.replace('Headless', '')); | ||
await Promise.all([this.send('Runtime.enable'), this.send('Page.setLifecycleEventsEnabled', { | ||
let autoAttachTarget = { | ||
waitForDebuggerOnStart: false, | ||
autoAttach: true, | ||
flatten: true | ||
}; | ||
await Promise.all([this.send('Runtime.enable'), this.send('Target.setAutoAttach', autoAttachTarget), this.send('Page.setLifecycleEventsEnabled', { | ||
enabled: true | ||
@@ -182,5 +142,2 @@ }), this.send('Network.setCacheDisabled', { | ||
value: !enableJavaScript | ||
}), this.resize({ | ||
width, | ||
height | ||
})]); | ||
@@ -197,7 +154,7 @@ | ||
async close() { | ||
if (!_classPrivateFieldGet(this, _browser)) return; | ||
if (!this.browser) return; | ||
/* istanbul ignore next: errors race here when the browser closes */ | ||
await _classPrivateFieldGet(this, _browser).send('Target.closeTarget', { | ||
targetId: _classPrivateFieldGet(this, _targetId) | ||
await this.browser.send('Target.closeTarget', { | ||
targetId: this.targetId | ||
}).catch(error => this.log.debug(error, this.meta)); | ||
@@ -209,4 +166,4 @@ } | ||
mobile = false, | ||
height = 1024, | ||
width = 1280 | ||
height, | ||
width | ||
}) { | ||
@@ -226,38 +183,45 @@ this.log.debug(`Resize page to ${width}x${height}`); | ||
} = {}) { | ||
let handleNavigate = event => { | ||
/* istanbul ignore else: sanity check */ | ||
if (_classPrivateFieldGet(this, _frameId) === event.frame.id) handleNavigate.done = true; | ||
}; // set cookies before navigation so we can default the domain to this hostname | ||
this.log.debug(`Navigate to: ${url}`, this.meta); | ||
let navigate = async () => { | ||
// set cookies before navigation so we can default the domain to this hostname | ||
if (this.browser.cookies.length) { | ||
let defaultDomain = (0, _utils.hostname)(url); | ||
await this.send('Network.setCookies', { | ||
// spread is used to make a shallow copy of the cookie | ||
cookies: this.browser.cookies.map(({ ...cookie | ||
}) => { | ||
if (!cookie.url) cookie.domain || (cookie.domain = defaultDomain); | ||
return cookie; | ||
}) | ||
}); | ||
} // handle navigation errors | ||
if (_classPrivateFieldGet(this, _browser).cookies.length) { | ||
let defaultDomain = (0, _utils.hostname)(url); | ||
await this.send('Network.setCookies', { | ||
// spread is used to make a shallow copy of the cookie | ||
cookies: _classPrivateFieldGet(this, _browser).cookies.map(({ ...cookie | ||
}) => { | ||
if (!cookie.url) cookie.domain || (cookie.domain = defaultDomain); | ||
return cookie; | ||
}) | ||
let res = await this.send('Page.navigate', { | ||
url | ||
}); | ||
} | ||
if (res.errorText) throw new Error(res.errorText); | ||
}; | ||
try { | ||
this.once('Page.frameNavigated', handleNavigate); | ||
this.log.debug(`Navigate to: ${url}`, this.meta); // trigger navigation and handle error responses | ||
let handlers = [// wait until navigation and the correct lifecycle | ||
['Page.frameNavigated', e => this.frameId === e.frame.id], ['Page.lifecycleEvent', e => this.frameId === e.frameId && e.name === waitUntil]].map(([name, cond]) => { | ||
let handler = e => cond(e) && (handler.finished = true) && handler.off(); | ||
let navigate = this.send('Page.navigate', { | ||
url | ||
}).then(({ | ||
errorText | ||
}) => { | ||
if (errorText) throw new Error(errorText); | ||
}); // wait until navigation was handled and the correct lifecycle happened | ||
handler.off = () => this.off(name, handler); | ||
await Promise.all([navigate, (0, _utils.waitFor)(() => { | ||
this.on(name, handler); | ||
return handler; | ||
}); | ||
try { | ||
// trigger navigation and poll for handlers to have finished | ||
await Promise.all([navigate(), (0, _utils.waitFor)(() => { | ||
if (this.closedReason) throw new Error(this.closedReason); | ||
return handleNavigate.done && _classPrivateFieldGet(this, _lifecycle).has(waitUntil); | ||
return handlers.every(handler => handler.finished); | ||
}, PAGE_TIMEOUT)]); | ||
} catch (error) { | ||
this.off('Page.frameNavigated', handleNavigate); | ||
// remove handlers and modify the error message | ||
for (let handler of handlers) handler.off(); | ||
throw Object.assign(error, { | ||
@@ -301,3 +265,3 @@ message: `Navigation failed: ${error.message}` | ||
})), | ||
executionContextId: _classPrivateFieldGet(this, _contextId), | ||
executionContextId: this.contextId, | ||
returnByValue: true, | ||
@@ -386,4 +350,4 @@ awaitPromise: true, | ||
let id = await _classPrivateFieldGet(this, _browser).send({ | ||
sessionId: _classPrivateFieldGet(this, _sessionId), | ||
let id = await this.browser.send({ | ||
sessionId: this.sessionId, | ||
method, | ||
@@ -437,3 +401,3 @@ params | ||
_classPrivateFieldSet(this, _browser, null); | ||
this.browser = null; | ||
} | ||
@@ -440,0 +404,0 @@ |
@@ -342,4 +342,2 @@ "use strict"; | ||
meta, | ||
// initial width | ||
width: widths.shift(), | ||
// enable network inteception | ||
@@ -361,11 +359,19 @@ intercept: { | ||
} | ||
}); // navigate to the url and trigger resize events | ||
}); // set the initial page size | ||
await page.goto(url); | ||
await page.resize({ | ||
width: widths.shift(), | ||
height: conf.minHeight | ||
}); // navigate to the url | ||
for (let width of widths) await page.resize({ | ||
width | ||
}); // create and add a percy-css resource | ||
await page.goto(url); // trigger resize events for other widths | ||
for (let width of widths) { | ||
await page.resize({ | ||
width, | ||
height: conf.minHeight | ||
}); | ||
} // create and add a percy-css resource | ||
let percyCSS = (0, _utils.createPercyCSSResource)(url, conf.percyCSS); | ||
@@ -372,0 +378,0 @@ if (percyCSS) resources.set(percyCSS.url, percyCSS); |
{ | ||
"name": "@percy/core", | ||
"version": "1.0.0-beta.63", | ||
"version": "1.0.0-beta.64", | ||
"license": "MIT", | ||
@@ -28,6 +28,6 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@percy/client": "1.0.0-beta.63", | ||
"@percy/config": "1.0.0-beta.63", | ||
"@percy/dom": "1.0.0-beta.63", | ||
"@percy/logger": "1.0.0-beta.63", | ||
"@percy/client": "1.0.0-beta.64", | ||
"@percy/config": "1.0.0-beta.64", | ||
"@percy/dom": "1.0.0-beta.64", | ||
"@percy/logger": "1.0.0-beta.64", | ||
"cross-spawn": "^7.0.3", | ||
@@ -43,3 +43,3 @@ "extract-zip": "^2.0.1", | ||
}, | ||
"gitHead": "9a20f918001deb9ce45f46f4560b3f50827789cd" | ||
"gitHead": "120c61edb91632976b0c795b532afe208b9b1dd7" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
103643
2272
+ Added@percy/client@1.0.0-beta.64(transitive)
+ Added@percy/config@1.0.0-beta.64(transitive)
+ Added@percy/dom@1.0.0-beta.64(transitive)
+ Added@percy/env@1.0.0-beta.64(transitive)
+ Added@percy/logger@1.0.0-beta.64(transitive)
- Removed@percy/client@1.0.0-beta.63(transitive)
- Removed@percy/config@1.0.0-beta.63(transitive)
- Removed@percy/dom@1.0.0-beta.63(transitive)
- Removed@percy/env@1.0.0-beta.63(transitive)
- Removed@percy/logger@1.0.0-beta.63(transitive)
Updated@percy/client@1.0.0-beta.64
Updated@percy/config@1.0.0-beta.64
Updated@percy/dom@1.0.0-beta.64
Updated@percy/logger@1.0.0-beta.64