UNPKG

testplane

Version:

Tests framework based on mocha and wdio

129 lines 6.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JSSelectivity = void 0; const lodash_1 = require("lodash"); const node_url_1 = require("node:url"); const constants_1 = require("../../../error-snippets/constants"); const utils_1 = require("./utils"); const SOURCE_CODE_EXTENSIONS = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".mts", ".cts"]; const isSourceCodeFile = (sourceFileName) => { return SOURCE_CODE_EXTENSIONS.some(ext => sourceFileName.endsWith(ext)); }; class JSSelectivity { constructor(cdp, sessionId, sourceRoot = "") { this._debuggerOnPausedFn = null; this._debuggerOnScriptParsedFn = null; this._scriptsSource = {}; this._scriptsSourceMap = {}; this._cdp = cdp; this._sessionId = sessionId; this._sourceRoot = sourceRoot; } _processScript({ scriptId, url, sourceMapURL }) { if (!this._sessionId) { return; } if (!url || !sourceMapURL) { this._scriptsSource[scriptId] ||= null; this._scriptsSourceMap[scriptId] ||= null; return; } this._scriptsSource[scriptId] ||= this._cdp.debugger .getScriptSource(this._sessionId, scriptId) .then(res => res.scriptSource) .catch((err) => err); this._scriptsSourceMap[scriptId] ||= (0, utils_1.fetchTextWithBrowserFallback)((0, node_url_1.resolve)(url, sourceMapURL), this._cdp.runtime, this._sessionId); } async start() { const debuggerOnPaused = (this._debuggerOnPausedFn = async () => { return this._cdp.debugger.resume(this._sessionId).catch(() => { }); }); const debuggerOnScriptParsedFn = (this._debuggerOnScriptParsedFn = this._processScript.bind(this)); const sessionId = this._sessionId; this._cdp.debugger.on("paused", debuggerOnPaused); this._cdp.debugger.on("scriptParsed", debuggerOnScriptParsedFn); await Promise.all([ this._cdp.target.setAutoAttach(sessionId, { autoAttach: true, waitForDebuggerOnStart: false }), this._cdp.debugger.enable(sessionId), this._cdp.profiler.enable(sessionId).then(() => this._cdp.profiler.startPreciseCoverage(sessionId, { callCount: false, detailed: false, allowTriggeredUpdates: false, })), ]); } /** @param drop only performs cleanup without providing actual deps. Should be "true" if test is failed */ async stop(drop) { if (drop) { this._debuggerOnPausedFn && this._cdp.debugger.off("paused", this._debuggerOnPausedFn); this._debuggerOnScriptParsedFn && this._cdp.debugger.off("scriptParsed", this._debuggerOnScriptParsedFn); return null; } const coverage = await this._cdp.profiler.takePreciseCoverage(this._sessionId); const scriptsWithUrl = coverage.result.filter(script => script.url); // If we haven't got "scriptParsed" event for the script, pull up source code + source map manually scriptsWithUrl.forEach(({ scriptId, url }) => { if (Object.hasOwn(this._scriptsSource, scriptId) && Object.hasOwn(this._scriptsSourceMap, scriptId)) { return; } const scriptSourcePromise = this._cdp.debugger .getScriptSource(this._sessionId, scriptId) .then(res => res.scriptSource) .catch((err) => err); this._scriptsSource[scriptId] ||= scriptSourcePromise; this._scriptsSourceMap[scriptId] ||= scriptSourcePromise.then(sourceCode => { if (sourceCode instanceof Error) { return sourceCode; } const sourceMapsStartIndex = sourceCode.lastIndexOf(constants_1.JS_SOURCE_MAP_URL_COMMENT); const sourceMapsEndIndex = sourceCode.indexOf("\n", sourceMapsStartIndex); if (sourceMapsStartIndex === -1) { return new Error("Source maping url comment is missing"); } const sourceMapURL = sourceMapsEndIndex === -1 ? sourceCode.slice(sourceMapsStartIndex + constants_1.JS_SOURCE_MAP_URL_COMMENT.length) : sourceCode.slice(sourceMapsStartIndex + constants_1.JS_SOURCE_MAP_URL_COMMENT.length, sourceMapsEndIndex); return (0, utils_1.fetchTextWithBrowserFallback)((0, node_url_1.resolve)(url, sourceMapURL), this._cdp.runtime, this._sessionId); }); }); const totalDependingSourceFiles = new Set(); const grouppedByScriptCoverage = (0, lodash_1.groupBy)(coverage.result, "scriptId"); const scriptIds = Object.keys(grouppedByScriptCoverage); await Promise.all(scriptIds.map(async (scriptId) => { // Every "scriptId" has only one uniq "url" const url = grouppedByScriptCoverage[scriptId][0].url; const [source, sourceMaps] = await Promise.all([ this._scriptsSource[scriptId], this._scriptsSourceMap[scriptId], ]); if (!source || !sourceMaps) { return; } if (source instanceof Error) { throw new Error(`JS Selectivity: Couldn't load source code at ${url}: ${source}`); } if (sourceMaps instanceof Error) { throw new Error(`JS Selectivity: Couldn't load source maps of ${url}: ${sourceMaps}`); } const startOffsetsSet = new Set(); grouppedByScriptCoverage[scriptId].forEach(entry => { entry.functions.forEach(fn => { fn.ranges.forEach(range => { startOffsetsSet.add(range.startOffset); }); }); }); const dependingSourceFiles = await (0, utils_1.extractSourceFilesDeps)(source, sourceMaps, Array.from(startOffsetsSet), this._sourceRoot); for (const sourceFile of dependingSourceFiles.values()) { if (isSourceCodeFile(sourceFile)) { totalDependingSourceFiles.add(sourceFile); } } })); this._debuggerOnPausedFn && this._cdp.debugger.off("paused", this._debuggerOnPausedFn); this._debuggerOnScriptParsedFn && this._cdp.debugger.off("scriptParsed", this._debuggerOnScriptParsedFn); return totalDependingSourceFiles; } } exports.JSSelectivity = JSSelectivity; //# sourceMappingURL=js-selectivity.js.map