mirror of
https://github.com/TheBinaryNinja/tvapp2.git
synced 2026-06-04 20:45:42 -04:00
build: push tvapp v2 docker files
This commit is contained in:
161
node_modules/playwright-core/lib/server/trace/recorder/snapshotter.js
generated
vendored
Normal file
161
node_modules/playwright-core/lib/server/trace/recorder/snapshotter.js
generated
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Snapshotter = void 0;
|
||||
var _browserContext = require("../../browserContext");
|
||||
var _page = require("../../page");
|
||||
var _eventsHelper = require("../../../utils/eventsHelper");
|
||||
var _debugLogger = require("../../../utils/debugLogger");
|
||||
var _snapshotterInjected = require("./snapshotterInjected");
|
||||
var _utils = require("../../../utils");
|
||||
var _utilsBundle = require("../../../utilsBundle");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class Snapshotter {
|
||||
constructor(context, delegate) {
|
||||
this._context = void 0;
|
||||
this._delegate = void 0;
|
||||
this._eventListeners = [];
|
||||
this._snapshotStreamer = void 0;
|
||||
this._initialized = false;
|
||||
this._started = false;
|
||||
this._context = context;
|
||||
this._delegate = delegate;
|
||||
const guid = (0, _utils.createGuid)();
|
||||
this._snapshotStreamer = '__playwright_snapshot_streamer_' + guid;
|
||||
}
|
||||
started() {
|
||||
return this._started;
|
||||
}
|
||||
async start() {
|
||||
this._started = true;
|
||||
if (!this._initialized) {
|
||||
this._initialized = true;
|
||||
await this._initialize();
|
||||
}
|
||||
await this.reset();
|
||||
}
|
||||
async reset() {
|
||||
if (this._started) await this._runInAllFrames(`window["${this._snapshotStreamer}"].reset()`);
|
||||
}
|
||||
async stop() {
|
||||
this._started = false;
|
||||
}
|
||||
resetForReuse() {
|
||||
// Next time we start recording, we will call addInitScript again.
|
||||
this._initialized = false;
|
||||
}
|
||||
async _initialize() {
|
||||
for (const page of this._context.pages()) this._onPage(page);
|
||||
this._eventListeners = [_eventsHelper.eventsHelper.addEventListener(this._context, _browserContext.BrowserContext.Events.Page, this._onPage.bind(this))];
|
||||
const {
|
||||
javaScriptEnabled
|
||||
} = this._context._options;
|
||||
const initScript = `(${_snapshotterInjected.frameSnapshotStreamer})("${this._snapshotStreamer}", ${javaScriptEnabled || javaScriptEnabled === undefined})`;
|
||||
await this._context.addInitScript(initScript);
|
||||
await this._runInAllFrames(initScript);
|
||||
}
|
||||
async _runInAllFrames(expression) {
|
||||
const frames = [];
|
||||
for (const page of this._context.pages()) frames.push(...page.frames());
|
||||
await Promise.all(frames.map(frame => {
|
||||
return frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => _debugLogger.debugLogger.log('error', e));
|
||||
}));
|
||||
}
|
||||
dispose() {
|
||||
_eventsHelper.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
}
|
||||
async captureSnapshot(page, callId, snapshotName) {
|
||||
// Prepare expression synchronously.
|
||||
const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${JSON.stringify(snapshotName)})`;
|
||||
|
||||
// In each frame, in a non-stalling manner, capture the snapshots.
|
||||
const snapshots = page.frames().map(async frame => {
|
||||
const data = await frame.nonStallingRawEvaluateInExistingMainContext(expression).catch(e => _debugLogger.debugLogger.log('error', e));
|
||||
// Something went wrong -> bail out, our snapshots are best-efforty.
|
||||
if (!data || !this._started) return;
|
||||
const snapshot = {
|
||||
callId,
|
||||
snapshotName,
|
||||
pageId: page.guid,
|
||||
frameId: frame.guid,
|
||||
frameUrl: data.url,
|
||||
doctype: data.doctype,
|
||||
html: data.html,
|
||||
viewport: data.viewport,
|
||||
timestamp: (0, _utils.monotonicTime)(),
|
||||
wallTime: data.wallTime,
|
||||
collectionTime: data.collectionTime,
|
||||
resourceOverrides: [],
|
||||
isMainFrame: page.mainFrame() === frame
|
||||
};
|
||||
for (const {
|
||||
url,
|
||||
content,
|
||||
contentType
|
||||
} of data.resourceOverrides) {
|
||||
if (typeof content === 'string') {
|
||||
const buffer = Buffer.from(content);
|
||||
const sha1 = (0, _utils.calculateSha1)(buffer) + '.' + (_utilsBundle.mime.getExtension(contentType) || 'dat');
|
||||
this._delegate.onSnapshotterBlob({
|
||||
sha1,
|
||||
buffer
|
||||
});
|
||||
snapshot.resourceOverrides.push({
|
||||
url,
|
||||
sha1
|
||||
});
|
||||
} else {
|
||||
snapshot.resourceOverrides.push({
|
||||
url,
|
||||
ref: content
|
||||
});
|
||||
}
|
||||
}
|
||||
this._delegate.onFrameSnapshot(snapshot);
|
||||
});
|
||||
await Promise.all(snapshots);
|
||||
}
|
||||
_onPage(page) {
|
||||
// Annotate frame hierarchy so that snapshots could include frame ids.
|
||||
for (const frame of page.frames()) this._annotateFrameHierarchy(frame);
|
||||
this._eventListeners.push(_eventsHelper.eventsHelper.addEventListener(page, _page.Page.Events.FrameAttached, frame => this._annotateFrameHierarchy(frame)));
|
||||
}
|
||||
async _annotateFrameHierarchy(frame) {
|
||||
try {
|
||||
const frameElement = await frame.frameElement();
|
||||
const parent = frame.parentFrame();
|
||||
if (!parent) return;
|
||||
const context = await parent._mainContext();
|
||||
await (context === null || context === void 0 ? void 0 : context.evaluate(({
|
||||
snapshotStreamer,
|
||||
frameElement,
|
||||
frameId
|
||||
}) => {
|
||||
window[snapshotStreamer].markIframe(frameElement, frameId);
|
||||
}, {
|
||||
snapshotStreamer: this._snapshotStreamer,
|
||||
frameElement,
|
||||
frameId: frame.guid
|
||||
}));
|
||||
frameElement.dispose();
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
exports.Snapshotter = Snapshotter;
|
||||
511
node_modules/playwright-core/lib/server/trace/recorder/snapshotterInjected.js
generated
vendored
Normal file
511
node_modules/playwright-core/lib/server/trace/recorder/snapshotterInjected.js
generated
vendored
Normal file
@@ -0,0 +1,511 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.frameSnapshotStreamer = frameSnapshotStreamer;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function frameSnapshotStreamer(snapshotStreamer, removeNoScript) {
|
||||
// Communication with Playwright.
|
||||
if (window[snapshotStreamer]) return;
|
||||
|
||||
// Attributes present in the snapshot.
|
||||
const kShadowAttribute = '__playwright_shadow_root_';
|
||||
const kValueAttribute = '__playwright_value_';
|
||||
const kCheckedAttribute = '__playwright_checked_';
|
||||
const kSelectedAttribute = '__playwright_selected_';
|
||||
const kScrollTopAttribute = '__playwright_scroll_top_';
|
||||
const kScrollLeftAttribute = '__playwright_scroll_left_';
|
||||
const kStyleSheetAttribute = '__playwright_style_sheet_';
|
||||
const kTargetAttribute = '__playwright_target__';
|
||||
const kCustomElementsAttribute = '__playwright_custom_elements__';
|
||||
const kCurrentSrcAttribute = '__playwright_current_src__';
|
||||
const kBoundingRectAttribute = '__playwright_bounding_rect__';
|
||||
|
||||
// Symbols for our own info on Nodes/StyleSheets.
|
||||
const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
|
||||
const kCachedData = Symbol('__playwright_snapshot_cache_');
|
||||
const kEndOfList = Symbol('__playwright_end_of_list_');
|
||||
function resetCachedData(obj) {
|
||||
delete obj[kCachedData];
|
||||
}
|
||||
function ensureCachedData(obj) {
|
||||
if (!obj[kCachedData]) obj[kCachedData] = {};
|
||||
return obj[kCachedData];
|
||||
}
|
||||
function removeHash(url) {
|
||||
try {
|
||||
const u = new URL(url);
|
||||
u.hash = '';
|
||||
return u.toString();
|
||||
} catch (e) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
class Streamer {
|
||||
constructor() {
|
||||
this._lastSnapshotNumber = 0;
|
||||
this._staleStyleSheets = new Set();
|
||||
this._readingStyleSheet = false;
|
||||
// To avoid invalidating due to our own reads.
|
||||
this._fakeBase = void 0;
|
||||
this._observer = void 0;
|
||||
const invalidateCSSGroupingRule = rule => {
|
||||
if (rule.parentStyleSheet) this._invalidateStyleSheet(rule.parentStyleSheet);
|
||||
};
|
||||
this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'insertRule', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'deleteRule', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'addRule', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'removeRule', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._interceptNativeGetter(window.CSSStyleSheet.prototype, 'rules', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._interceptNativeGetter(window.CSSStyleSheet.prototype, 'cssRules', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._interceptNativeMethod(window.CSSStyleSheet.prototype, 'replaceSync', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._interceptNativeMethod(window.CSSGroupingRule.prototype, 'insertRule', invalidateCSSGroupingRule);
|
||||
this._interceptNativeMethod(window.CSSGroupingRule.prototype, 'deleteRule', invalidateCSSGroupingRule);
|
||||
this._interceptNativeGetter(window.CSSGroupingRule.prototype, 'cssRules', invalidateCSSGroupingRule);
|
||||
this._interceptNativeAsyncMethod(window.CSSStyleSheet.prototype, 'replace', sheet => this._invalidateStyleSheet(sheet));
|
||||
this._fakeBase = document.createElement('base');
|
||||
this._observer = new MutationObserver(list => this._handleMutations(list));
|
||||
const observerConfig = {
|
||||
attributes: true,
|
||||
subtree: true
|
||||
};
|
||||
this._observer.observe(document, observerConfig);
|
||||
this._refreshListenersWhenNeeded();
|
||||
}
|
||||
_refreshListenersWhenNeeded() {
|
||||
this._refreshListeners();
|
||||
const customEventName = '__playwright_snapshotter_global_listeners_check__';
|
||||
let seenEvent = false;
|
||||
const handleCustomEvent = () => seenEvent = true;
|
||||
window.addEventListener(customEventName, handleCustomEvent);
|
||||
const observer = new MutationObserver(entries => {
|
||||
// Check for new documentElement in case we need to reinstall document listeners.
|
||||
const newDocumentElement = entries.some(entry => Array.from(entry.addedNodes).includes(document.documentElement));
|
||||
if (newDocumentElement) {
|
||||
// New documentElement - let's check whether listeners are still here.
|
||||
seenEvent = false;
|
||||
window.dispatchEvent(new CustomEvent(customEventName));
|
||||
if (!seenEvent) {
|
||||
// Listener did not fire. Reattach the listener and notify.
|
||||
window.addEventListener(customEventName, handleCustomEvent);
|
||||
this._refreshListeners();
|
||||
}
|
||||
}
|
||||
});
|
||||
observer.observe(document, {
|
||||
childList: true
|
||||
});
|
||||
}
|
||||
_refreshListeners() {
|
||||
document.addEventListener('__playwright_mark_target__', event => {
|
||||
if (!event.detail) return;
|
||||
const callId = event.detail;
|
||||
event.composedPath()[0].__playwright_target__ = callId;
|
||||
});
|
||||
document.addEventListener('__playwright_unmark_target__', event => {
|
||||
if (!event.detail) return;
|
||||
const callId = event.detail;
|
||||
if (event.composedPath()[0].__playwright_target__ === callId) delete event.composedPath()[0].__playwright_target__;
|
||||
});
|
||||
}
|
||||
_interceptNativeMethod(obj, method, cb) {
|
||||
const native = obj[method];
|
||||
if (!native) return;
|
||||
obj[method] = function (...args) {
|
||||
const result = native.call(this, ...args);
|
||||
cb(this, result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
_interceptNativeAsyncMethod(obj, method, cb) {
|
||||
const native = obj[method];
|
||||
if (!native) return;
|
||||
obj[method] = async function (...args) {
|
||||
const result = await native.call(this, ...args);
|
||||
cb(this, result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
_interceptNativeGetter(obj, prop, cb) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(obj, prop);
|
||||
Object.defineProperty(obj, prop, {
|
||||
...descriptor,
|
||||
get: function () {
|
||||
const result = descriptor.get.call(this);
|
||||
cb(this, result);
|
||||
return result;
|
||||
}
|
||||
});
|
||||
}
|
||||
_handleMutations(list) {
|
||||
for (const mutation of list) ensureCachedData(mutation.target).attributesCached = undefined;
|
||||
}
|
||||
_invalidateStyleSheet(sheet) {
|
||||
if (this._readingStyleSheet) return;
|
||||
this._staleStyleSheets.add(sheet);
|
||||
}
|
||||
_updateStyleElementStyleSheetTextIfNeeded(sheet, forceText) {
|
||||
const data = ensureCachedData(sheet);
|
||||
if (this._staleStyleSheets.has(sheet) || forceText && data.cssText === undefined) {
|
||||
this._staleStyleSheets.delete(sheet);
|
||||
try {
|
||||
data.cssText = this._getSheetText(sheet);
|
||||
} catch (e) {
|
||||
// Sometimes we cannot access cross-origin stylesheets.
|
||||
data.cssText = '';
|
||||
}
|
||||
}
|
||||
return data.cssText;
|
||||
}
|
||||
|
||||
// Returns either content, ref, or no override.
|
||||
_updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber) {
|
||||
const data = ensureCachedData(sheet);
|
||||
if (this._staleStyleSheets.has(sheet)) {
|
||||
this._staleStyleSheets.delete(sheet);
|
||||
try {
|
||||
data.cssText = this._getSheetText(sheet);
|
||||
data.cssRef = snapshotNumber;
|
||||
return data.cssText;
|
||||
} catch (e) {
|
||||
// Sometimes we cannot access cross-origin stylesheets.
|
||||
}
|
||||
}
|
||||
return data.cssRef === undefined ? undefined : snapshotNumber - data.cssRef;
|
||||
}
|
||||
markIframe(iframeElement, frameId) {
|
||||
iframeElement[kSnapshotFrameId] = frameId;
|
||||
}
|
||||
reset() {
|
||||
this._staleStyleSheets.clear();
|
||||
const visitNode = node => {
|
||||
resetCachedData(node);
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
const element = node;
|
||||
if (element.shadowRoot) visitNode(element.shadowRoot);
|
||||
}
|
||||
for (let child = node.firstChild; child; child = child.nextSibling) visitNode(child);
|
||||
};
|
||||
visitNode(document.documentElement);
|
||||
visitNode(this._fakeBase);
|
||||
}
|
||||
__sanitizeMetaAttribute(name, value, httpEquiv) {
|
||||
if (name === 'charset') return 'utf-8';
|
||||
if (httpEquiv.toLowerCase() !== 'content-type' || name !== 'content') return value;
|
||||
const [type, ...params] = value.split(';');
|
||||
if (type !== 'text/html' || params.length <= 0) return value;
|
||||
const charsetParamIdx = params.findIndex(param => param.trim().startsWith('charset='));
|
||||
if (charsetParamIdx > -1) params[charsetParamIdx] = 'charset=utf-8';
|
||||
return `${type}; ${params.join('; ')}`;
|
||||
}
|
||||
_sanitizeUrl(url) {
|
||||
if (url.startsWith('javascript:') || url.startsWith('vbscript:')) return '';
|
||||
return url;
|
||||
}
|
||||
_sanitizeSrcSet(srcset) {
|
||||
return srcset.split(',').map(src => {
|
||||
src = src.trim();
|
||||
const spaceIndex = src.lastIndexOf(' ');
|
||||
if (spaceIndex === -1) return this._sanitizeUrl(src);
|
||||
return this._sanitizeUrl(src.substring(0, spaceIndex).trim()) + src.substring(spaceIndex);
|
||||
}).join(', ');
|
||||
}
|
||||
_resolveUrl(base, url) {
|
||||
if (url === '') return '';
|
||||
try {
|
||||
return new URL(url, base).href;
|
||||
} catch (e) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
_getSheetBase(sheet) {
|
||||
let rootSheet = sheet;
|
||||
while (rootSheet.parentStyleSheet) rootSheet = rootSheet.parentStyleSheet;
|
||||
if (rootSheet.ownerNode) return rootSheet.ownerNode.baseURI;
|
||||
return document.baseURI;
|
||||
}
|
||||
_getSheetText(sheet) {
|
||||
this._readingStyleSheet = true;
|
||||
try {
|
||||
const rules = [];
|
||||
for (const rule of sheet.cssRules) rules.push(rule.cssText);
|
||||
return rules.join('\n');
|
||||
} finally {
|
||||
this._readingStyleSheet = false;
|
||||
}
|
||||
}
|
||||
captureSnapshot() {
|
||||
const timestamp = performance.now();
|
||||
const snapshotNumber = ++this._lastSnapshotNumber;
|
||||
let nodeCounter = 0;
|
||||
let shadowDomNesting = 0;
|
||||
let headNesting = 0;
|
||||
|
||||
// Ensure we are up to date.
|
||||
this._handleMutations(this._observer.takeRecords());
|
||||
const definedCustomElements = new Set();
|
||||
const visitNode = node => {
|
||||
const nodeType = node.nodeType;
|
||||
const nodeName = nodeType === Node.DOCUMENT_FRAGMENT_NODE ? 'template' : node.nodeName;
|
||||
if (nodeType !== Node.ELEMENT_NODE && nodeType !== Node.DOCUMENT_FRAGMENT_NODE && nodeType !== Node.TEXT_NODE) return;
|
||||
if (nodeName === 'SCRIPT') return;
|
||||
// Don't preload resources.
|
||||
if (nodeName === 'LINK' && nodeType === Node.ELEMENT_NODE) {
|
||||
var _getAttribute;
|
||||
const rel = (_getAttribute = node.getAttribute('rel')) === null || _getAttribute === void 0 ? void 0 : _getAttribute.toLowerCase();
|
||||
if (rel === 'preload' || rel === 'prefetch') return;
|
||||
}
|
||||
if (removeNoScript && nodeName === 'NOSCRIPT') return;
|
||||
if (nodeName === 'META' && node.httpEquiv.toLowerCase() === 'content-security-policy') return;
|
||||
// Skip iframes which are inside document's head as they are not visible.
|
||||
// See https://github.com/microsoft/playwright/issues/12005.
|
||||
if ((nodeName === 'IFRAME' || nodeName === 'FRAME') && headNesting) return;
|
||||
const data = ensureCachedData(node);
|
||||
const values = [];
|
||||
let equals = !!data.cached;
|
||||
let extraNodes = 0;
|
||||
const expectValue = value => {
|
||||
equals = equals && data.cached[values.length] === value;
|
||||
values.push(value);
|
||||
};
|
||||
const checkAndReturn = n => {
|
||||
data.attributesCached = true;
|
||||
if (equals) return {
|
||||
equals: true,
|
||||
n: [[snapshotNumber - data.ref[0], data.ref[1]]]
|
||||
};
|
||||
nodeCounter += extraNodes;
|
||||
data.ref = [snapshotNumber, nodeCounter++];
|
||||
data.cached = values;
|
||||
return {
|
||||
equals: false,
|
||||
n
|
||||
};
|
||||
};
|
||||
if (nodeType === Node.TEXT_NODE) {
|
||||
const value = node.nodeValue || '';
|
||||
expectValue(value);
|
||||
return checkAndReturn(value);
|
||||
}
|
||||
if (nodeName === 'STYLE') {
|
||||
const sheet = node.sheet;
|
||||
let cssText;
|
||||
if (sheet) cssText = this._updateStyleElementStyleSheetTextIfNeeded(sheet);
|
||||
cssText = cssText || node.textContent || '';
|
||||
expectValue(cssText);
|
||||
// Compensate for the extra 'cssText' text node.
|
||||
extraNodes++;
|
||||
return checkAndReturn([nodeName, {}, cssText]);
|
||||
}
|
||||
const attrs = {};
|
||||
const result = [nodeName, attrs];
|
||||
const visitChild = child => {
|
||||
const snapshot = visitNode(child);
|
||||
if (snapshot) {
|
||||
result.push(snapshot.n);
|
||||
expectValue(child);
|
||||
equals = equals && snapshot.equals;
|
||||
}
|
||||
};
|
||||
const visitChildStyleSheet = child => {
|
||||
const snapshot = visitStyleSheet(child);
|
||||
if (snapshot) {
|
||||
result.push(snapshot.n);
|
||||
expectValue(child);
|
||||
equals = equals && snapshot.equals;
|
||||
}
|
||||
};
|
||||
if (nodeType === Node.DOCUMENT_FRAGMENT_NODE) attrs[kShadowAttribute] = 'open';
|
||||
if (nodeType === Node.ELEMENT_NODE) {
|
||||
var _window$customElement;
|
||||
const element = node;
|
||||
if (element.localName.includes('-') && (_window$customElement = window.customElements) !== null && _window$customElement !== void 0 && _window$customElement.get(element.localName)) definedCustomElements.add(element.localName);
|
||||
if (nodeName === 'INPUT' || nodeName === 'TEXTAREA') {
|
||||
const value = element.value;
|
||||
expectValue(kValueAttribute);
|
||||
expectValue(value);
|
||||
attrs[kValueAttribute] = value;
|
||||
}
|
||||
if (nodeName === 'INPUT' && ['checkbox', 'radio'].includes(element.type)) {
|
||||
const value = element.checked ? 'true' : 'false';
|
||||
expectValue(kCheckedAttribute);
|
||||
expectValue(value);
|
||||
attrs[kCheckedAttribute] = value;
|
||||
}
|
||||
if (nodeName === 'OPTION') {
|
||||
const value = element.selected ? 'true' : 'false';
|
||||
expectValue(kSelectedAttribute);
|
||||
expectValue(value);
|
||||
attrs[kSelectedAttribute] = value;
|
||||
}
|
||||
if (nodeName === 'CANVAS') {
|
||||
const boundingRect = element.getBoundingClientRect();
|
||||
const value = JSON.stringify({
|
||||
left: boundingRect.left / window.innerWidth,
|
||||
top: boundingRect.top / window.innerHeight,
|
||||
right: boundingRect.right / window.innerWidth,
|
||||
bottom: boundingRect.bottom / window.innerHeight
|
||||
});
|
||||
expectValue(kBoundingRectAttribute);
|
||||
expectValue(value);
|
||||
attrs[kBoundingRectAttribute] = value;
|
||||
}
|
||||
if (element.scrollTop) {
|
||||
expectValue(kScrollTopAttribute);
|
||||
expectValue(element.scrollTop);
|
||||
attrs[kScrollTopAttribute] = '' + element.scrollTop;
|
||||
}
|
||||
if (element.scrollLeft) {
|
||||
expectValue(kScrollLeftAttribute);
|
||||
expectValue(element.scrollLeft);
|
||||
attrs[kScrollLeftAttribute] = '' + element.scrollLeft;
|
||||
}
|
||||
if (element.shadowRoot) {
|
||||
++shadowDomNesting;
|
||||
visitChild(element.shadowRoot);
|
||||
--shadowDomNesting;
|
||||
}
|
||||
if ('__playwright_target__' in element) {
|
||||
expectValue(kTargetAttribute);
|
||||
expectValue(element['__playwright_target__']);
|
||||
attrs[kTargetAttribute] = element['__playwright_target__'];
|
||||
}
|
||||
}
|
||||
if (nodeName === 'HEAD') {
|
||||
++headNesting;
|
||||
// Insert fake <base> first, to ensure all <link> elements use the proper base uri.
|
||||
this._fakeBase.setAttribute('href', document.baseURI);
|
||||
visitChild(this._fakeBase);
|
||||
}
|
||||
for (let child = node.firstChild; child; child = child.nextSibling) visitChild(child);
|
||||
if (nodeName === 'HEAD') --headNesting;
|
||||
expectValue(kEndOfList);
|
||||
let documentOrShadowRoot = null;
|
||||
if (node.ownerDocument.documentElement === node) documentOrShadowRoot = node.ownerDocument;else if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) documentOrShadowRoot = node;
|
||||
if (documentOrShadowRoot) {
|
||||
for (const sheet of documentOrShadowRoot.adoptedStyleSheets || []) visitChildStyleSheet(sheet);
|
||||
expectValue(kEndOfList);
|
||||
}
|
||||
|
||||
// Process iframe src attribute before bailing out since it depends on a symbol, not the DOM.
|
||||
if (nodeName === 'IFRAME' || nodeName === 'FRAME') {
|
||||
const element = node;
|
||||
const frameId = element[kSnapshotFrameId];
|
||||
const name = 'src';
|
||||
const value = frameId ? `/snapshot/${frameId}` : '';
|
||||
expectValue(name);
|
||||
expectValue(value);
|
||||
attrs[name] = value;
|
||||
}
|
||||
|
||||
// Process custom elements before bailing out since they depend on JS, not the DOM.
|
||||
if (nodeName === 'BODY' && definedCustomElements.size) {
|
||||
const value = [...definedCustomElements].join(',');
|
||||
expectValue(kCustomElementsAttribute);
|
||||
expectValue(value);
|
||||
attrs[kCustomElementsAttribute] = value;
|
||||
}
|
||||
|
||||
// Process currentSrc before bailing out since it depends on JS, not the DOM.
|
||||
if (nodeName === 'IMG' || nodeName === 'PICTURE') {
|
||||
const value = nodeName === 'PICTURE' ? '' : this._sanitizeUrl(node.currentSrc);
|
||||
expectValue(kCurrentSrcAttribute);
|
||||
expectValue(value);
|
||||
attrs[kCurrentSrcAttribute] = value;
|
||||
}
|
||||
|
||||
// We can skip attributes comparison because nothing else has changed,
|
||||
// and mutation observer didn't tell us about the attributes.
|
||||
if (equals && data.attributesCached && !shadowDomNesting) return checkAndReturn(result);
|
||||
if (nodeType === Node.ELEMENT_NODE) {
|
||||
const element = node;
|
||||
for (let i = 0; i < element.attributes.length; i++) {
|
||||
const name = element.attributes[i].name;
|
||||
if (nodeName === 'LINK' && name === 'integrity') continue;
|
||||
if (nodeName === 'IFRAME' && (name === 'src' || name === 'srcdoc' || name === 'sandbox')) continue;
|
||||
if (nodeName === 'FRAME' && name === 'src') continue;
|
||||
let value = element.attributes[i].value;
|
||||
if (nodeName === 'META') value = this.__sanitizeMetaAttribute(name, value, node.httpEquiv);else if (name === 'src' && nodeName === 'IMG') value = this._sanitizeUrl(value);else if (name === 'srcset' && nodeName === 'IMG') value = this._sanitizeSrcSet(value);else if (name === 'srcset' && nodeName === 'SOURCE') value = this._sanitizeSrcSet(value);else if (name === 'href' && nodeName === 'LINK') value = this._sanitizeUrl(value);else if (name.startsWith('on')) value = '';
|
||||
expectValue(name);
|
||||
expectValue(value);
|
||||
attrs[name] = value;
|
||||
}
|
||||
expectValue(kEndOfList);
|
||||
}
|
||||
if (result.length === 2 && !Object.keys(attrs).length) result.pop(); // Remove empty attrs when there are no children.
|
||||
return checkAndReturn(result);
|
||||
};
|
||||
const visitStyleSheet = sheet => {
|
||||
const data = ensureCachedData(sheet);
|
||||
const oldCSSText = data.cssText;
|
||||
const cssText = this._updateStyleElementStyleSheetTextIfNeeded(sheet, true /* forceText */);
|
||||
if (cssText === oldCSSText) return {
|
||||
equals: true,
|
||||
n: [[snapshotNumber - data.ref[0], data.ref[1]]]
|
||||
};
|
||||
data.ref = [snapshotNumber, nodeCounter++];
|
||||
return {
|
||||
equals: false,
|
||||
n: ['template', {
|
||||
[kStyleSheetAttribute]: cssText
|
||||
}]
|
||||
};
|
||||
};
|
||||
let html;
|
||||
if (document.documentElement) {
|
||||
const {
|
||||
n
|
||||
} = visitNode(document.documentElement);
|
||||
html = n;
|
||||
} else {
|
||||
html = ['html'];
|
||||
}
|
||||
const result = {
|
||||
html,
|
||||
doctype: document.doctype ? document.doctype.name : undefined,
|
||||
resourceOverrides: [],
|
||||
viewport: {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
},
|
||||
url: location.href,
|
||||
wallTime: Date.now(),
|
||||
collectionTime: 0
|
||||
};
|
||||
for (const sheet of this._staleStyleSheets) {
|
||||
if (sheet.href === null) continue;
|
||||
const content = this._updateLinkStyleSheetTextIfNeeded(sheet, snapshotNumber);
|
||||
if (content === undefined) {
|
||||
// Unable to capture stylesheet contents.
|
||||
continue;
|
||||
}
|
||||
const base = this._getSheetBase(sheet);
|
||||
const url = removeHash(this._resolveUrl(base, sheet.href));
|
||||
result.resourceOverrides.push({
|
||||
url,
|
||||
content,
|
||||
contentType: 'text/css'
|
||||
});
|
||||
}
|
||||
result.collectionTime = performance.now() - timestamp;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
window[snapshotStreamer] = new Streamer();
|
||||
}
|
||||
596
node_modules/playwright-core/lib/server/trace/recorder/tracing.js
generated
vendored
Normal file
596
node_modules/playwright-core/lib/server/trace/recorder/tracing.js
generated
vendored
Normal file
@@ -0,0 +1,596 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.Tracing = void 0;
|
||||
exports.shouldCaptureSnapshot = shouldCaptureSnapshot;
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _os = _interopRequireDefault(require("os"));
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _debug = require("../../../protocol/debug");
|
||||
var _utils = require("../../../utils");
|
||||
var _artifact = require("../../artifact");
|
||||
var _browserContext = require("../../browserContext");
|
||||
var _instrumentation = require("../../instrumentation");
|
||||
var _page = require("../../page");
|
||||
var _harTracer = require("../../har/harTracer");
|
||||
var _snapshotter = require("./snapshotter");
|
||||
var _dispatcher = require("../../dispatchers/dispatcher");
|
||||
var _errors = require("../../errors");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const version = 7;
|
||||
const kScreencastOptions = {
|
||||
width: 800,
|
||||
height: 600,
|
||||
quality: 90
|
||||
};
|
||||
class Tracing extends _instrumentation.SdkObject {
|
||||
constructor(context, tracesDir) {
|
||||
super(context, 'tracing');
|
||||
this._fs = new _utils.SerializedFS();
|
||||
this._snapshotter = void 0;
|
||||
this._harTracer = void 0;
|
||||
this._screencastListeners = [];
|
||||
this._eventListeners = [];
|
||||
this._context = void 0;
|
||||
// Note: state should only be touched inside API methods, but not inside trace operations.
|
||||
this._state = void 0;
|
||||
this._isStopping = false;
|
||||
this._precreatedTracesDir = void 0;
|
||||
this._tracesTmpDir = void 0;
|
||||
this._allResources = new Set();
|
||||
this._contextCreatedEvent = void 0;
|
||||
this._pendingHarEntries = new Set();
|
||||
this._context = context;
|
||||
this._precreatedTracesDir = tracesDir;
|
||||
this._harTracer = new _harTracer.HarTracer(context, null, this, {
|
||||
content: 'attach',
|
||||
includeTraceInfo: true,
|
||||
recordRequestOverrides: false,
|
||||
waitForContentOnStop: false
|
||||
});
|
||||
const testIdAttributeName = 'selectors' in context ? context.selectors().testIdAttributeName() : undefined;
|
||||
this._contextCreatedEvent = {
|
||||
version,
|
||||
type: 'context-options',
|
||||
origin: 'library',
|
||||
browserName: '',
|
||||
options: {},
|
||||
platform: process.platform,
|
||||
wallTime: 0,
|
||||
monotonicTime: 0,
|
||||
sdkLanguage: context.attribution.playwright.options.sdkLanguage,
|
||||
testIdAttributeName
|
||||
};
|
||||
if (context instanceof _browserContext.BrowserContext) {
|
||||
this._snapshotter = new _snapshotter.Snapshotter(context, this);
|
||||
(0, _utils.assert)(tracesDir, 'tracesDir must be specified for BrowserContext');
|
||||
this._contextCreatedEvent.browserName = context._browser.options.name;
|
||||
this._contextCreatedEvent.channel = context._browser.options.channel;
|
||||
this._contextCreatedEvent.options = context._options;
|
||||
}
|
||||
}
|
||||
async resetForReuse() {
|
||||
var _this$_snapshotter;
|
||||
// Discard previous chunk if any and ignore any errors there.
|
||||
await this.stopChunk({
|
||||
mode: 'discard'
|
||||
}).catch(() => {});
|
||||
await this.stop();
|
||||
(_this$_snapshotter = this._snapshotter) === null || _this$_snapshotter === void 0 || _this$_snapshotter.resetForReuse();
|
||||
}
|
||||
async start(options) {
|
||||
if (this._isStopping) throw new Error('Cannot start tracing while stopping');
|
||||
if (this._state) throw new Error('Tracing has been already started');
|
||||
|
||||
// Re-write for testing.
|
||||
this._contextCreatedEvent.sdkLanguage = this._context.attribution.playwright.options.sdkLanguage;
|
||||
|
||||
// TODO: passing the same name for two contexts makes them write into a single file
|
||||
// and conflict.
|
||||
const traceName = options.name || (0, _utils.createGuid)();
|
||||
const tracesDir = this._createTracesDirIfNeeded();
|
||||
|
||||
// Init the state synchronously.
|
||||
this._state = {
|
||||
options,
|
||||
traceName,
|
||||
tracesDir,
|
||||
traceFile: _path.default.join(tracesDir, traceName + '.trace'),
|
||||
networkFile: _path.default.join(tracesDir, traceName + '.network'),
|
||||
resourcesDir: _path.default.join(tracesDir, 'resources'),
|
||||
chunkOrdinal: 0,
|
||||
traceSha1s: new Set(),
|
||||
networkSha1s: new Set(),
|
||||
recording: false,
|
||||
callIds: new Set(),
|
||||
groupStack: []
|
||||
};
|
||||
this._fs.mkdir(this._state.resourcesDir);
|
||||
this._fs.writeFile(this._state.networkFile, '');
|
||||
// Tracing is 10x bigger if we include scripts in every trace.
|
||||
if (options.snapshots) this._harTracer.start({
|
||||
omitScripts: !options.live
|
||||
});
|
||||
}
|
||||
async startChunk(options = {}) {
|
||||
var _this$_snapshotter2;
|
||||
if (this._state && this._state.recording) await this.stopChunk({
|
||||
mode: 'discard'
|
||||
});
|
||||
if (!this._state) throw new Error('Must start tracing before starting a new chunk');
|
||||
if (this._isStopping) throw new Error('Cannot start a trace chunk while stopping');
|
||||
this._state.recording = true;
|
||||
this._state.callIds.clear();
|
||||
if (options.name && options.name !== this._state.traceName) this._changeTraceName(this._state, options.name);else this._allocateNewTraceFile(this._state);
|
||||
this._fs.mkdir(_path.default.dirname(this._state.traceFile));
|
||||
const event = {
|
||||
...this._contextCreatedEvent,
|
||||
title: options.title,
|
||||
wallTime: Date.now(),
|
||||
monotonicTime: (0, _utils.monotonicTime)()
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
this._context.instrumentation.addListener(this, this._context);
|
||||
this._eventListeners.push(_utils.eventsHelper.addEventListener(this._context, _browserContext.BrowserContext.Events.Console, this._onConsoleMessage.bind(this)), _utils.eventsHelper.addEventListener(this._context, _browserContext.BrowserContext.Events.PageError, this._onPageError.bind(this)));
|
||||
if (this._state.options.screenshots) this._startScreencast();
|
||||
if (this._state.options.snapshots) await ((_this$_snapshotter2 = this._snapshotter) === null || _this$_snapshotter2 === void 0 ? void 0 : _this$_snapshotter2.start());
|
||||
return {
|
||||
traceName: this._state.traceName
|
||||
};
|
||||
}
|
||||
_currentGroupId() {
|
||||
var _this$_state;
|
||||
return (_this$_state = this._state) !== null && _this$_state !== void 0 && _this$_state.groupStack.length ? this._state.groupStack[this._state.groupStack.length - 1] : undefined;
|
||||
}
|
||||
async group(name, location, metadata) {
|
||||
var _ref;
|
||||
if (!this._state) return;
|
||||
const stackFrames = [];
|
||||
const {
|
||||
file,
|
||||
line,
|
||||
column
|
||||
} = (_ref = location !== null && location !== void 0 ? location : metadata.location) !== null && _ref !== void 0 ? _ref : {};
|
||||
if (file) {
|
||||
stackFrames.push({
|
||||
file,
|
||||
line: line !== null && line !== void 0 ? line : 0,
|
||||
column: column !== null && column !== void 0 ? column : 0
|
||||
});
|
||||
}
|
||||
const event = {
|
||||
type: 'before',
|
||||
callId: metadata.id,
|
||||
startTime: metadata.startTime,
|
||||
apiName: name,
|
||||
class: 'Tracing',
|
||||
method: 'tracingGroup',
|
||||
params: {},
|
||||
stepId: metadata.stepId,
|
||||
stack: stackFrames
|
||||
};
|
||||
if (this._currentGroupId()) event.parentId = this._currentGroupId();
|
||||
this._state.groupStack.push(event.callId);
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
groupEnd() {
|
||||
if (!this._state) return;
|
||||
const callId = this._state.groupStack.pop();
|
||||
if (!callId) return;
|
||||
const event = {
|
||||
type: 'after',
|
||||
callId,
|
||||
endTime: (0, _utils.monotonicTime)()
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
_startScreencast() {
|
||||
if (!(this._context instanceof _browserContext.BrowserContext)) return;
|
||||
for (const page of this._context.pages()) this._startScreencastInPage(page);
|
||||
this._screencastListeners.push(_utils.eventsHelper.addEventListener(this._context, _browserContext.BrowserContext.Events.Page, this._startScreencastInPage.bind(this)));
|
||||
}
|
||||
_stopScreencast() {
|
||||
_utils.eventsHelper.removeEventListeners(this._screencastListeners);
|
||||
if (!(this._context instanceof _browserContext.BrowserContext)) return;
|
||||
for (const page of this._context.pages()) page.setScreencastOptions(null);
|
||||
}
|
||||
_allocateNewTraceFile(state) {
|
||||
const suffix = state.chunkOrdinal ? `-chunk${state.chunkOrdinal}` : ``;
|
||||
state.chunkOrdinal++;
|
||||
state.traceFile = _path.default.join(state.tracesDir, `${state.traceName}${suffix}.trace`);
|
||||
}
|
||||
_changeTraceName(state, name) {
|
||||
state.traceName = name;
|
||||
state.chunkOrdinal = 0; // Reset ordinal for the new name.
|
||||
this._allocateNewTraceFile(state);
|
||||
|
||||
// Network file survives across chunks, so make a copy with the new name.
|
||||
const newNetworkFile = _path.default.join(state.tracesDir, name + '.network');
|
||||
this._fs.copyFile(state.networkFile, newNetworkFile);
|
||||
state.networkFile = newNetworkFile;
|
||||
}
|
||||
async stop() {
|
||||
if (!this._state) return;
|
||||
if (this._isStopping) throw new Error(`Tracing is already stopping`);
|
||||
if (this._state.recording) throw new Error(`Must stop trace file before stopping tracing`);
|
||||
this._closeAllGroups();
|
||||
this._harTracer.stop();
|
||||
this.flushHarEntries();
|
||||
await this._fs.syncAndGetError();
|
||||
this._state = undefined;
|
||||
}
|
||||
async deleteTmpTracesDir() {
|
||||
if (this._tracesTmpDir) await (0, _utils.removeFolders)([this._tracesTmpDir]);
|
||||
}
|
||||
_createTracesDirIfNeeded() {
|
||||
if (this._precreatedTracesDir) return this._precreatedTracesDir;
|
||||
this._tracesTmpDir = _fs.default.mkdtempSync(_path.default.join(_os.default.tmpdir(), 'playwright-tracing-'));
|
||||
return this._tracesTmpDir;
|
||||
}
|
||||
abort() {
|
||||
var _this$_snapshotter3;
|
||||
(_this$_snapshotter3 = this._snapshotter) === null || _this$_snapshotter3 === void 0 || _this$_snapshotter3.dispose();
|
||||
this._harTracer.stop();
|
||||
}
|
||||
async flush() {
|
||||
this.abort();
|
||||
await this._fs.syncAndGetError();
|
||||
}
|
||||
_closeAllGroups() {
|
||||
while (this._currentGroupId()) this.groupEnd();
|
||||
}
|
||||
async stopChunk(params) {
|
||||
var _this$_snapshotter4;
|
||||
if (this._isStopping) throw new Error(`Tracing is already stopping`);
|
||||
this._isStopping = true;
|
||||
if (!this._state || !this._state.recording) {
|
||||
this._isStopping = false;
|
||||
if (params.mode !== 'discard') throw new Error(`Must start tracing before stopping`);
|
||||
return {};
|
||||
}
|
||||
this._closeAllGroups();
|
||||
this._context.instrumentation.removeListener(this);
|
||||
_utils.eventsHelper.removeEventListeners(this._eventListeners);
|
||||
if (this._state.options.screenshots) this._stopScreencast();
|
||||
if (this._state.options.snapshots) await ((_this$_snapshotter4 = this._snapshotter) === null || _this$_snapshotter4 === void 0 ? void 0 : _this$_snapshotter4.stop());
|
||||
this.flushHarEntries();
|
||||
|
||||
// Network file survives across chunks, make a snapshot before returning the resulting entries.
|
||||
// We should pick a name starting with "traceName" and ending with .network.
|
||||
// Something like <traceName>someSuffixHere.network.
|
||||
// However, this name must not clash with any other "traceName".network in the same tracesDir.
|
||||
// We can use <traceName>-<guid>.network, but "-pwnetcopy-0" suffix is more readable
|
||||
// and makes it easier to debug future issues.
|
||||
const newNetworkFile = _path.default.join(this._state.tracesDir, this._state.traceName + `-pwnetcopy-${this._state.chunkOrdinal}.network`);
|
||||
const entries = [];
|
||||
entries.push({
|
||||
name: 'trace.trace',
|
||||
value: this._state.traceFile
|
||||
});
|
||||
entries.push({
|
||||
name: 'trace.network',
|
||||
value: newNetworkFile
|
||||
});
|
||||
for (const sha1 of new Set([...this._state.traceSha1s, ...this._state.networkSha1s])) entries.push({
|
||||
name: _path.default.join('resources', sha1),
|
||||
value: _path.default.join(this._state.resourcesDir, sha1)
|
||||
});
|
||||
|
||||
// Only reset trace sha1s, network resources are preserved between chunks.
|
||||
this._state.traceSha1s = new Set();
|
||||
if (params.mode === 'discard') {
|
||||
this._isStopping = false;
|
||||
this._state.recording = false;
|
||||
return {};
|
||||
}
|
||||
this._fs.copyFile(this._state.networkFile, newNetworkFile);
|
||||
const zipFileName = this._state.traceFile + '.zip';
|
||||
if (params.mode === 'archive') this._fs.zip(entries, zipFileName);
|
||||
|
||||
// Make sure all file operations complete.
|
||||
const error = await this._fs.syncAndGetError();
|
||||
this._isStopping = false;
|
||||
if (this._state) this._state.recording = false;
|
||||
|
||||
// IMPORTANT: no awaits after this point, to make sure recording state is correct.
|
||||
|
||||
if (error) {
|
||||
// This check is here because closing the browser removes the tracesDir and tracing
|
||||
// cannot access removed files. Clients are ready for the missing artifact.
|
||||
if (this._context instanceof _browserContext.BrowserContext && !this._context._browser.isConnected()) return {};
|
||||
throw error;
|
||||
}
|
||||
if (params.mode === 'entries') return {
|
||||
entries
|
||||
};
|
||||
const artifact = new _artifact.Artifact(this._context, zipFileName);
|
||||
artifact.reportFinished();
|
||||
return {
|
||||
artifact
|
||||
};
|
||||
}
|
||||
async _captureSnapshot(snapshotName, sdkObject, metadata) {
|
||||
if (!this._snapshotter) return;
|
||||
if (!sdkObject.attribution.page) return;
|
||||
if (!this._snapshotter.started()) return;
|
||||
if (!shouldCaptureSnapshot(metadata)) return;
|
||||
await this._snapshotter.captureSnapshot(sdkObject.attribution.page, metadata.id, snapshotName).catch(() => {});
|
||||
}
|
||||
onBeforeCall(sdkObject, metadata) {
|
||||
var _sdkObject$attributio, _this$_state2;
|
||||
// IMPORTANT: no awaits before this._appendTraceEvent in this method.
|
||||
const event = createBeforeActionTraceEvent(metadata, this._currentGroupId());
|
||||
if (!event) return Promise.resolve();
|
||||
(_sdkObject$attributio = sdkObject.attribution.page) === null || _sdkObject$attributio === void 0 || _sdkObject$attributio.temporarilyDisableTracingScreencastThrottling();
|
||||
event.beforeSnapshot = `before@${metadata.id}`;
|
||||
(_this$_state2 = this._state) === null || _this$_state2 === void 0 || _this$_state2.callIds.add(metadata.id);
|
||||
this._appendTraceEvent(event);
|
||||
return this._captureSnapshot(event.beforeSnapshot, sdkObject, metadata);
|
||||
}
|
||||
onBeforeInputAction(sdkObject, metadata) {
|
||||
var _this$_state3, _sdkObject$attributio2;
|
||||
if (!((_this$_state3 = this._state) !== null && _this$_state3 !== void 0 && _this$_state3.callIds.has(metadata.id))) return Promise.resolve();
|
||||
// IMPORTANT: no awaits before this._appendTraceEvent in this method.
|
||||
const event = createInputActionTraceEvent(metadata);
|
||||
if (!event) return Promise.resolve();
|
||||
(_sdkObject$attributio2 = sdkObject.attribution.page) === null || _sdkObject$attributio2 === void 0 || _sdkObject$attributio2.temporarilyDisableTracingScreencastThrottling();
|
||||
event.inputSnapshot = `input@${metadata.id}`;
|
||||
this._appendTraceEvent(event);
|
||||
return this._captureSnapshot(event.inputSnapshot, sdkObject, metadata);
|
||||
}
|
||||
onCallLog(sdkObject, metadata, logName, message) {
|
||||
if (metadata.isServerSide || metadata.internal) return;
|
||||
if (logName !== 'api') return;
|
||||
const event = createActionLogTraceEvent(metadata, message);
|
||||
if (event) this._appendTraceEvent(event);
|
||||
}
|
||||
async onAfterCall(sdkObject, metadata) {
|
||||
var _this$_state4, _this$_state5, _sdkObject$attributio3;
|
||||
if (!((_this$_state4 = this._state) !== null && _this$_state4 !== void 0 && _this$_state4.callIds.has(metadata.id))) return;
|
||||
(_this$_state5 = this._state) === null || _this$_state5 === void 0 || _this$_state5.callIds.delete(metadata.id);
|
||||
const event = createAfterActionTraceEvent(metadata);
|
||||
if (!event) return;
|
||||
(_sdkObject$attributio3 = sdkObject.attribution.page) === null || _sdkObject$attributio3 === void 0 || _sdkObject$attributio3.temporarilyDisableTracingScreencastThrottling();
|
||||
event.afterSnapshot = `after@${metadata.id}`;
|
||||
this._appendTraceEvent(event);
|
||||
return this._captureSnapshot(event.afterSnapshot, sdkObject, metadata);
|
||||
}
|
||||
onEntryStarted(entry) {
|
||||
this._pendingHarEntries.add(entry);
|
||||
}
|
||||
onEntryFinished(entry) {
|
||||
this._pendingHarEntries.delete(entry);
|
||||
const event = {
|
||||
type: 'resource-snapshot',
|
||||
snapshot: entry
|
||||
};
|
||||
const visited = visitTraceEvent(event, this._state.networkSha1s);
|
||||
this._fs.appendFile(this._state.networkFile, JSON.stringify(visited) + '\n', true /* flush */);
|
||||
}
|
||||
flushHarEntries() {
|
||||
const harLines = [];
|
||||
for (const entry of this._pendingHarEntries) {
|
||||
const event = {
|
||||
type: 'resource-snapshot',
|
||||
snapshot: entry
|
||||
};
|
||||
const visited = visitTraceEvent(event, this._state.networkSha1s);
|
||||
harLines.push(JSON.stringify(visited));
|
||||
}
|
||||
this._pendingHarEntries.clear();
|
||||
if (harLines.length) this._fs.appendFile(this._state.networkFile, harLines.join('\n') + '\n', true /* flush */);
|
||||
}
|
||||
onContentBlob(sha1, buffer) {
|
||||
this._appendResource(sha1, buffer);
|
||||
}
|
||||
onSnapshotterBlob(blob) {
|
||||
this._appendResource(blob.sha1, blob.buffer);
|
||||
}
|
||||
onFrameSnapshot(snapshot) {
|
||||
this._appendTraceEvent({
|
||||
type: 'frame-snapshot',
|
||||
snapshot
|
||||
});
|
||||
}
|
||||
_onConsoleMessage(message) {
|
||||
var _message$page;
|
||||
const event = {
|
||||
type: 'console',
|
||||
messageType: message.type(),
|
||||
text: message.text(),
|
||||
args: message.args().map(a => ({
|
||||
preview: a.toString(),
|
||||
value: a.rawValue()
|
||||
})),
|
||||
location: message.location(),
|
||||
time: (0, _utils.monotonicTime)(),
|
||||
pageId: (_message$page = message.page()) === null || _message$page === void 0 ? void 0 : _message$page.guid
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
onDialog(dialog) {
|
||||
const event = {
|
||||
type: 'event',
|
||||
time: (0, _utils.monotonicTime)(),
|
||||
class: 'BrowserContext',
|
||||
method: 'dialog',
|
||||
params: {
|
||||
pageId: dialog.page().guid,
|
||||
type: dialog.type(),
|
||||
message: dialog.message(),
|
||||
defaultValue: dialog.defaultValue()
|
||||
}
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
onDownload(page, download) {
|
||||
const event = {
|
||||
type: 'event',
|
||||
time: (0, _utils.monotonicTime)(),
|
||||
class: 'BrowserContext',
|
||||
method: 'download',
|
||||
params: {
|
||||
pageId: page.guid,
|
||||
url: download.url,
|
||||
suggestedFilename: download.suggestedFilename()
|
||||
}
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
onPageOpen(page) {
|
||||
var _page$opener;
|
||||
const event = {
|
||||
type: 'event',
|
||||
time: (0, _utils.monotonicTime)(),
|
||||
class: 'BrowserContext',
|
||||
method: 'page',
|
||||
params: {
|
||||
pageId: page.guid,
|
||||
openerPageId: (_page$opener = page.opener()) === null || _page$opener === void 0 ? void 0 : _page$opener.guid
|
||||
}
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
onPageClose(page) {
|
||||
const event = {
|
||||
type: 'event',
|
||||
time: (0, _utils.monotonicTime)(),
|
||||
class: 'BrowserContext',
|
||||
method: 'pageClosed',
|
||||
params: {
|
||||
pageId: page.guid
|
||||
}
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
_onPageError(error, page) {
|
||||
const event = {
|
||||
type: 'event',
|
||||
time: (0, _utils.monotonicTime)(),
|
||||
class: 'BrowserContext',
|
||||
method: 'pageError',
|
||||
params: {
|
||||
error: (0, _errors.serializeError)(error)
|
||||
},
|
||||
pageId: page.guid
|
||||
};
|
||||
this._appendTraceEvent(event);
|
||||
}
|
||||
_startScreencastInPage(page) {
|
||||
page.setScreencastOptions(kScreencastOptions);
|
||||
const prefix = page.guid;
|
||||
this._screencastListeners.push(_utils.eventsHelper.addEventListener(page, _page.Page.Events.ScreencastFrame, params => {
|
||||
const suffix = params.timestamp || Date.now();
|
||||
const sha1 = `${prefix}-${suffix}.jpeg`;
|
||||
const event = {
|
||||
type: 'screencast-frame',
|
||||
pageId: page.guid,
|
||||
sha1,
|
||||
width: params.width,
|
||||
height: params.height,
|
||||
timestamp: (0, _utils.monotonicTime)(),
|
||||
frameSwapWallTime: params.frameSwapWallTime
|
||||
};
|
||||
// Make sure to write the screencast frame before adding a reference to it.
|
||||
this._appendResource(sha1, params.buffer);
|
||||
this._appendTraceEvent(event);
|
||||
}));
|
||||
}
|
||||
_appendTraceEvent(event) {
|
||||
const visited = visitTraceEvent(event, this._state.traceSha1s);
|
||||
// Do not flush (console) events, they are too noisy, unless we are in ui mode (live).
|
||||
const flush = this._state.options.live || event.type !== 'event' && event.type !== 'console' && event.type !== 'log';
|
||||
this._fs.appendFile(this._state.traceFile, JSON.stringify(visited) + '\n', flush);
|
||||
}
|
||||
_appendResource(sha1, buffer) {
|
||||
if (this._allResources.has(sha1)) return;
|
||||
this._allResources.add(sha1);
|
||||
const resourcePath = _path.default.join(this._state.resourcesDir, sha1);
|
||||
this._fs.writeFile(resourcePath, buffer, true /* skipIfExists */);
|
||||
}
|
||||
}
|
||||
exports.Tracing = Tracing;
|
||||
function visitTraceEvent(object, sha1s) {
|
||||
if (Array.isArray(object)) return object.map(o => visitTraceEvent(o, sha1s));
|
||||
if (object instanceof _dispatcher.Dispatcher) return `<${object._type}>`;
|
||||
if (object instanceof Buffer) return `<Buffer>`;
|
||||
if (object instanceof Date) return object;
|
||||
if (typeof object === 'object') {
|
||||
const result = {};
|
||||
for (const key in object) {
|
||||
if (key === 'sha1' || key === '_sha1' || key.endsWith('Sha1')) {
|
||||
const sha1 = object[key];
|
||||
if (sha1) sha1s.add(sha1);
|
||||
}
|
||||
result[key] = visitTraceEvent(object[key], sha1s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
function shouldCaptureSnapshot(metadata) {
|
||||
return _debug.commandsWithTracingSnapshots.has(metadata.type + '.' + metadata.method);
|
||||
}
|
||||
function createBeforeActionTraceEvent(metadata, parentId) {
|
||||
if (metadata.internal || metadata.method.startsWith('tracing')) return null;
|
||||
const event = {
|
||||
type: 'before',
|
||||
callId: metadata.id,
|
||||
startTime: metadata.startTime,
|
||||
apiName: metadata.apiName || metadata.type + '.' + metadata.method,
|
||||
class: metadata.type,
|
||||
method: metadata.method,
|
||||
params: metadata.params,
|
||||
stepId: metadata.stepId,
|
||||
pageId: metadata.pageId
|
||||
};
|
||||
if (parentId) event.parentId = parentId;
|
||||
return event;
|
||||
}
|
||||
function createInputActionTraceEvent(metadata) {
|
||||
if (metadata.internal || metadata.method.startsWith('tracing')) return null;
|
||||
return {
|
||||
type: 'input',
|
||||
callId: metadata.id,
|
||||
point: metadata.point
|
||||
};
|
||||
}
|
||||
function createActionLogTraceEvent(metadata, message) {
|
||||
if (metadata.internal || metadata.method.startsWith('tracing')) return null;
|
||||
return {
|
||||
type: 'log',
|
||||
callId: metadata.id,
|
||||
time: (0, _utils.monotonicTime)(),
|
||||
message
|
||||
};
|
||||
}
|
||||
function createAfterActionTraceEvent(metadata) {
|
||||
var _metadata$error;
|
||||
if (metadata.internal || metadata.method.startsWith('tracing')) return null;
|
||||
return {
|
||||
type: 'after',
|
||||
callId: metadata.id,
|
||||
endTime: metadata.endTime,
|
||||
error: (_metadata$error = metadata.error) === null || _metadata$error === void 0 ? void 0 : _metadata$error.error,
|
||||
result: metadata.result,
|
||||
point: metadata.point
|
||||
};
|
||||
}
|
||||
93
node_modules/playwright-core/lib/server/trace/test/inMemorySnapshotter.js
generated
vendored
Normal file
93
node_modules/playwright-core/lib/server/trace/test/inMemorySnapshotter.js
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.InMemorySnapshotter = void 0;
|
||||
var _snapshotStorage = require("../../../../../trace-viewer/src/sw/snapshotStorage");
|
||||
var _snapshotter = require("../recorder/snapshotter");
|
||||
var _harTracer = require("../../har/harTracer");
|
||||
var _utils = require("../../../utils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class InMemorySnapshotter {
|
||||
constructor(context) {
|
||||
this._blobs = new Map();
|
||||
this._snapshotter = void 0;
|
||||
this._harTracer = void 0;
|
||||
this._snapshotReadyPromises = new Map();
|
||||
this._storage = void 0;
|
||||
this._snapshotCount = 0;
|
||||
this._snapshotter = new _snapshotter.Snapshotter(context, this);
|
||||
this._harTracer = new _harTracer.HarTracer(context, null, this, {
|
||||
content: 'attach',
|
||||
includeTraceInfo: true,
|
||||
recordRequestOverrides: false,
|
||||
waitForContentOnStop: false
|
||||
});
|
||||
this._storage = new _snapshotStorage.SnapshotStorage();
|
||||
}
|
||||
async initialize() {
|
||||
await this._snapshotter.start();
|
||||
this._harTracer.start({
|
||||
omitScripts: true
|
||||
});
|
||||
}
|
||||
async reset() {
|
||||
await this._snapshotter.reset();
|
||||
await this._harTracer.flush();
|
||||
this._harTracer.stop();
|
||||
this._harTracer.start({
|
||||
omitScripts: true
|
||||
});
|
||||
}
|
||||
async dispose() {
|
||||
this._snapshotter.dispose();
|
||||
await this._harTracer.flush();
|
||||
this._harTracer.stop();
|
||||
}
|
||||
async captureSnapshot(page, callId, snapshotName) {
|
||||
if (this._snapshotReadyPromises.has(snapshotName)) throw new Error('Duplicate snapshot name: ' + snapshotName);
|
||||
this._snapshotter.captureSnapshot(page, callId, snapshotName).catch(() => {});
|
||||
const promise = new _utils.ManualPromise();
|
||||
this._snapshotReadyPromises.set(snapshotName, promise);
|
||||
return promise;
|
||||
}
|
||||
onEntryStarted(entry) {}
|
||||
onEntryFinished(entry) {
|
||||
this._storage.addResource(entry);
|
||||
}
|
||||
onContentBlob(sha1, buffer) {
|
||||
this._blobs.set(sha1, buffer);
|
||||
}
|
||||
onSnapshotterBlob(blob) {
|
||||
this._blobs.set(blob.sha1, blob.buffer);
|
||||
}
|
||||
onFrameSnapshot(snapshot) {
|
||||
var _this$_snapshotReadyP;
|
||||
++this._snapshotCount;
|
||||
const renderer = this._storage.addFrameSnapshot(snapshot, []);
|
||||
(_this$_snapshotReadyP = this._snapshotReadyPromises.get(snapshot.snapshotName || '')) === null || _this$_snapshotReadyP === void 0 || _this$_snapshotReadyP.resolve(renderer);
|
||||
}
|
||||
async resourceContentForTest(sha1) {
|
||||
return this._blobs.get(sha1);
|
||||
}
|
||||
snapshotCount() {
|
||||
return this._snapshotCount;
|
||||
}
|
||||
}
|
||||
exports.InMemorySnapshotter = InMemorySnapshotter;
|
||||
214
node_modules/playwright-core/lib/server/trace/viewer/traceViewer.js
generated
vendored
Normal file
214
node_modules/playwright-core/lib/server/trace/viewer/traceViewer.js
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.installRootRedirect = installRootRedirect;
|
||||
exports.openTraceInBrowser = openTraceInBrowser;
|
||||
exports.openTraceViewerApp = openTraceViewerApp;
|
||||
exports.runTraceInBrowser = runTraceInBrowser;
|
||||
exports.runTraceViewerApp = runTraceViewerApp;
|
||||
exports.startTraceViewerServer = startTraceViewerServer;
|
||||
var _path = _interopRequireDefault(require("path"));
|
||||
var _fs = _interopRequireDefault(require("fs"));
|
||||
var _httpServer = require("../../../utils/httpServer");
|
||||
var _utils = require("../../../utils");
|
||||
var _launchApp = require("../../launchApp");
|
||||
var _instrumentation = require("../../instrumentation");
|
||||
var _playwright = require("../../playwright");
|
||||
var _progress = require("../../progress");
|
||||
var _utilsBundle = require("../../../utilsBundle");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
function validateTraceUrls(traceUrls) {
|
||||
for (const traceUrl of traceUrls) {
|
||||
let traceFile = traceUrl;
|
||||
// If .json is requested, we'll synthesize it.
|
||||
if (traceUrl.endsWith('.json')) traceFile = traceUrl.substring(0, traceUrl.length - '.json'.length);
|
||||
if (!traceUrl.startsWith('http://') && !traceUrl.startsWith('https://') && !_fs.default.existsSync(traceFile) && !_fs.default.existsSync(traceFile + '.trace')) throw new Error(`Trace file ${traceUrl} does not exist!`);
|
||||
}
|
||||
}
|
||||
async function startTraceViewerServer(options) {
|
||||
const server = new _httpServer.HttpServer();
|
||||
server.routePrefix('/trace', (request, response) => {
|
||||
const url = new URL('http://localhost' + request.url);
|
||||
const relativePath = url.pathname.slice('/trace'.length);
|
||||
if (relativePath.endsWith('/stall.js')) return true;
|
||||
if (relativePath.startsWith('/file')) {
|
||||
try {
|
||||
const filePath = url.searchParams.get('path');
|
||||
if (_fs.default.existsSync(filePath)) return server.serveFile(request, response, url.searchParams.get('path'));
|
||||
|
||||
// If .json is requested, we'll synthesize it for zip-less operation.
|
||||
if (filePath.endsWith('.json')) {
|
||||
const traceName = filePath.substring(0, filePath.length - '.json'.length);
|
||||
response.statusCode = 200;
|
||||
response.setHeader('Content-Type', 'application/json');
|
||||
response.end(JSON.stringify(traceDescriptor(traceName)));
|
||||
return true;
|
||||
}
|
||||
} catch (e) {}
|
||||
response.statusCode = 404;
|
||||
response.end();
|
||||
return true;
|
||||
}
|
||||
const absolutePath = _path.default.join(__dirname, '..', '..', '..', 'vite', 'traceViewer', ...relativePath.split('/'));
|
||||
return server.serveFile(request, response, absolutePath);
|
||||
});
|
||||
const transport = (options === null || options === void 0 ? void 0 : options.transport) || (options !== null && options !== void 0 && options.isServer ? new StdinServer() : undefined);
|
||||
if (transport) server.createWebSocket(transport);
|
||||
const {
|
||||
host,
|
||||
port
|
||||
} = options || {};
|
||||
await server.start({
|
||||
preferredPort: port,
|
||||
host
|
||||
});
|
||||
return server;
|
||||
}
|
||||
async function installRootRedirect(server, traceUrls, options) {
|
||||
const params = new URLSearchParams();
|
||||
if (_path.default.sep !== _path.default.posix.sep) params.set('pathSeparator', _path.default.sep);
|
||||
for (const traceUrl of traceUrls) params.append('trace', traceUrl);
|
||||
if (server.wsGuid()) params.append('ws', server.wsGuid());
|
||||
if (options !== null && options !== void 0 && options.isServer) params.append('isServer', '');
|
||||
if ((0, _utils.isUnderTest)()) params.append('isUnderTest', 'true');
|
||||
for (const arg of options.args || []) params.append('arg', arg);
|
||||
if (options.grep) params.append('grep', options.grep);
|
||||
if (options.grepInvert) params.append('grepInvert', options.grepInvert);
|
||||
for (const project of options.project || []) params.append('project', project);
|
||||
for (const reporter of options.reporter || []) params.append('reporter', reporter);
|
||||
const urlPath = `./trace/${options.webApp || 'index.html'}?${params.toString()}`;
|
||||
server.routePath('/', (_, response) => {
|
||||
response.statusCode = 302;
|
||||
response.setHeader('Location', urlPath);
|
||||
response.end();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
async function runTraceViewerApp(traceUrls, browserName, options, exitOnClose) {
|
||||
validateTraceUrls(traceUrls);
|
||||
const server = await startTraceViewerServer(options);
|
||||
await installRootRedirect(server, traceUrls, options);
|
||||
const page = await openTraceViewerApp(server.urlPrefix('precise'), browserName, options);
|
||||
if (exitOnClose) page.on('close', () => (0, _utils.gracefullyProcessExitDoNotHang)(0));
|
||||
return page;
|
||||
}
|
||||
async function runTraceInBrowser(traceUrls, options) {
|
||||
validateTraceUrls(traceUrls);
|
||||
const server = await startTraceViewerServer(options);
|
||||
await installRootRedirect(server, traceUrls, options);
|
||||
await openTraceInBrowser(server.urlPrefix('human-readable'));
|
||||
}
|
||||
async function openTraceViewerApp(url, browserName, options) {
|
||||
const traceViewerPlaywright = (0, _playwright.createPlaywright)({
|
||||
sdkLanguage: 'javascript',
|
||||
isInternalPlaywright: true
|
||||
});
|
||||
const traceViewerBrowser = (0, _utils.isUnderTest)() ? 'chromium' : browserName;
|
||||
const {
|
||||
context,
|
||||
page
|
||||
} = await (0, _launchApp.launchApp)(traceViewerPlaywright[traceViewerBrowser], {
|
||||
// TODO: store language in the trace.
|
||||
sdkLanguage: traceViewerPlaywright.options.sdkLanguage,
|
||||
windowSize: {
|
||||
width: 1280,
|
||||
height: 800
|
||||
},
|
||||
persistentContextOptions: {
|
||||
...(options === null || options === void 0 ? void 0 : options.persistentContextOptions),
|
||||
useWebSocket: (0, _utils.isUnderTest)(),
|
||||
headless: !!(options !== null && options !== void 0 && options.headless),
|
||||
colorScheme: (0, _utils.isUnderTest)() ? 'light' : undefined
|
||||
}
|
||||
});
|
||||
const controller = new _progress.ProgressController((0, _instrumentation.serverSideCallMetadata)(), context._browser);
|
||||
await controller.run(async progress => {
|
||||
await context._browser._defaultContext._loadDefaultContextAsIs(progress);
|
||||
});
|
||||
if (process.env.PWTEST_PRINT_WS_ENDPOINT) process.stderr.write('DevTools listening on: ' + context._browser.options.wsEndpoint + '\n');
|
||||
if (!(0, _utils.isUnderTest)()) await (0, _launchApp.syncLocalStorageWithSettings)(page, 'traceviewer');
|
||||
if ((0, _utils.isUnderTest)()) page.on('close', () => context.close({
|
||||
reason: 'Trace viewer closed'
|
||||
}).catch(() => {}));
|
||||
await page.mainFrame().goto((0, _instrumentation.serverSideCallMetadata)(), url);
|
||||
return page;
|
||||
}
|
||||
async function openTraceInBrowser(url) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\nListening on ' + url);
|
||||
if (!(0, _utils.isUnderTest)()) await (0, _utilsBundle.open)(url.replace('0.0.0.0', 'localhost')).catch(() => {});
|
||||
}
|
||||
class StdinServer {
|
||||
constructor() {
|
||||
this._pollTimer = void 0;
|
||||
this._traceUrl = void 0;
|
||||
this.sendEvent = void 0;
|
||||
this.close = void 0;
|
||||
process.stdin.on('data', data => {
|
||||
const url = data.toString().trim();
|
||||
if (url === this._traceUrl) return;
|
||||
if (url.endsWith('.json')) this._pollLoadTrace(url);else this._loadTrace(url);
|
||||
});
|
||||
process.stdin.on('close', () => (0, _utils.gracefullyProcessExitDoNotHang)(0));
|
||||
}
|
||||
onconnect() {}
|
||||
async dispatch(method, params) {
|
||||
if (method === 'initialize') {
|
||||
if (this._traceUrl) this._loadTrace(this._traceUrl);
|
||||
}
|
||||
}
|
||||
onclose() {}
|
||||
_loadTrace(traceUrl) {
|
||||
var _this$sendEvent;
|
||||
this._traceUrl = traceUrl;
|
||||
clearTimeout(this._pollTimer);
|
||||
(_this$sendEvent = this.sendEvent) === null || _this$sendEvent === void 0 || _this$sendEvent.call(this, 'loadTraceRequested', {
|
||||
traceUrl
|
||||
});
|
||||
}
|
||||
_pollLoadTrace(url) {
|
||||
this._loadTrace(url);
|
||||
this._pollTimer = setTimeout(() => {
|
||||
this._pollLoadTrace(url);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
function traceDescriptor(traceName) {
|
||||
const result = {
|
||||
entries: []
|
||||
};
|
||||
const traceDir = _path.default.dirname(traceName);
|
||||
const traceFile = _path.default.basename(traceName);
|
||||
for (const name of _fs.default.readdirSync(traceDir)) {
|
||||
if (name.startsWith(traceFile)) result.entries.push({
|
||||
name,
|
||||
path: _path.default.join(traceDir, name)
|
||||
});
|
||||
}
|
||||
const resourcesDir = _path.default.join(traceDir, 'resources');
|
||||
if (_fs.default.existsSync(resourcesDir)) {
|
||||
for (const name of _fs.default.readdirSync(resourcesDir)) result.entries.push({
|
||||
name: 'resources/' + name,
|
||||
path: _path.default.join(resourcesDir, name)
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user