UNPKG

chrome-remote-interface-extra

Version:

Bringing a puppeteer like API and more to the chrome-remote-interface by cyrus-and

119 lines (111 loc) 3.68 kB
const { helper, debugError, assert } = require('../helper') const { EVALUATION_SCRIPT_URL } = require('../executionContext') const { convertToDisjointRanges } = require('../__shared') class JSCoverage { /** * @param {Chrome|CRIConnection|CDPSession|Object} client */ constructor (client) { /** * @type {Chrome|CRIConnection|CDPSession|Object} * @private */ this._client = client /** * @type {boolean} * @private */ this._enabled = false this._scriptURLs = new Map() this._scriptSources = new Map() this._eventListeners = [] this._resetOnNavigation = false } /** * @param {!{resetOnNavigation?: boolean, reportAnonymousScripts?: boolean}} options */ async start (options = {}) { assert(!this._enabled, 'JSCoverage is already enabled') const { resetOnNavigation = true, reportAnonymousScripts = false } = options this._resetOnNavigation = resetOnNavigation this._reportAnonymousScripts = reportAnonymousScripts this._enabled = true this._scriptURLs.clear() this._scriptSources.clear() this._eventListeners = [ helper.addEventListener( this._client, 'Debugger.scriptParsed', this._onScriptParsed.bind(this) ), helper.addEventListener( this._client, 'Runtime.executionContextsCleared', this._onExecutionContextsCleared.bind(this) ) ] await Promise.all([ this._client.send('Profiler.enable'), this._client.send('Profiler.startPreciseCoverage', { callCount: false, detailed: true }), this._client.send('Debugger.enable'), this._client.send('Debugger.setSkipAllPauses', { skip: true }) ]) } _onExecutionContextsCleared () { if (!this._resetOnNavigation) return this._scriptURLs.clear() this._scriptSources.clear() } /** * @param {!Object} event */ async _onScriptParsed (event) { // Ignore puppeteer-injected scripts if (event.url === EVALUATION_SCRIPT_URL) return // Ignore other anonymous scripts unless the reportAnonymousScripts option is true. if (!event.url && !this._reportAnonymousScripts) return try { const response = await this._client.send('Debugger.getScriptSource', { scriptId: event.scriptId }) this._scriptURLs.set(event.scriptId, event.url) this._scriptSources.set(event.scriptId, response.scriptSource) } catch (e) { // This might happen if the page has already navigated away. debugError(e) console.error(e) } } /** * @return {Promise<Array<CoverageEntry>>} */ async stop () { assert(this._enabled, 'JSCoverage is not enabled') this._enabled = false const [profileResponse] = await Promise.all([ this._client.send('Profiler.takePreciseCoverage'), this._client.send('Profiler.stopPreciseCoverage'), this._client.send('Profiler.disable'), this._client.send('Debugger.disable') ]) helper.removeEventListeners(this._eventListeners) const coverage = [] for (const entry of profileResponse.result) { let url = this._scriptURLs.get(entry.scriptId) if (!url && this._reportAnonymousScripts) { url = 'debugger://VM' + entry.scriptId } const text = this._scriptSources.get(entry.scriptId) if (text === undefined || url === undefined) continue const flattenRanges = [] for (const func of entry.functions) flattenRanges.push(...func.ranges) const ranges = convertToDisjointRanges(flattenRanges) coverage.push({ url, ranges, text }) } return coverage } } module.exports = JSCoverage