diff --git a/src/common/Coverage.ts b/src/common/Coverage.ts index 5c7da9c26f7fe..70061be94ea8d 100644 --- a/src/common/Coverage.ts +++ b/src/common/Coverage.ts @@ -45,6 +45,17 @@ export interface CoverageEntry { ranges: Array<{ start: number; end: number }>; } +/** + * The CoverageEntry class for JavaScript + * @public + */ +export interface JSCoverageEntry extends CoverageEntry { + /** + * Raw V8 script coverage entry. + */ + rawScriptCoverage?: Protocol.Profiler.ScriptCoverage; +} + /** * Set of configurable options for JS coverage. * @public @@ -58,6 +69,10 @@ export interface JSCoverageOptions { * Whether anonymous scripts generated by the page should be reported. */ reportAnonymousScripts?: boolean; + /** + * Whether the result includes raw V8 script coverage entries. + */ + includeRawScriptCoverage?: boolean; } /** @@ -145,7 +160,7 @@ export class Coverage { * JavaScript Coverage doesn't include anonymous scripts by default. * However, scripts with sourceURLs are reported. */ - async stopJSCoverage(): Promise { + async stopJSCoverage(): Promise { return await this._jsCoverage.stop(); } @@ -181,6 +196,7 @@ export class JSCoverage { _eventListeners: PuppeteerEventListener[] = []; _resetOnNavigation = false; _reportAnonymousScripts = false; + _includeRawScriptCoverage = false; constructor(client: CDPSession) { this._client = client; @@ -190,13 +206,18 @@ export class JSCoverage { options: { resetOnNavigation?: boolean; reportAnonymousScripts?: boolean; + includeRawScriptCoverage?: boolean; } = {} ): Promise { assert(!this._enabled, 'JSCoverage is already enabled'); - const { resetOnNavigation = true, reportAnonymousScripts = false } = - options; + const { + resetOnNavigation = true, + reportAnonymousScripts = false, + includeRawScriptCoverage = false, + } = options; this._resetOnNavigation = resetOnNavigation; this._reportAnonymousScripts = reportAnonymousScripts; + this._includeRawScriptCoverage = includeRawScriptCoverage; this._enabled = true; this._scriptURLs.clear(); this._scriptSources.clear(); @@ -215,7 +236,7 @@ export class JSCoverage { await Promise.all([ this._client.send('Profiler.enable'), this._client.send('Profiler.startPreciseCoverage', { - callCount: false, + callCount: this._includeRawScriptCoverage, detailed: true, }), this._client.send('Debugger.enable'), @@ -248,7 +269,7 @@ export class JSCoverage { } } - async stop(): Promise { + async stop(): Promise { assert(this._enabled, 'JSCoverage is not enabled'); this._enabled = false; @@ -278,7 +299,11 @@ export class JSCoverage { const flattenRanges = []; for (const func of entry.functions) flattenRanges.push(...func.ranges); const ranges = convertToDisjointRanges(flattenRanges); - coverage.push({ url, ranges, text }); + if (!this._includeRawScriptCoverage) { + coverage.push({ url, ranges, text }); + } else { + coverage.push({ url, ranges, text, rawScriptCoverage: entry }); + } } return coverage; } diff --git a/test/coverage.spec.ts b/test/coverage.spec.ts index a5ff0dc0f57df..4801a8eed5816 100644 --- a/test/coverage.spec.ts +++ b/test/coverage.spec.ts @@ -157,6 +157,41 @@ describe('Coverage specs', function () { expect(coverage.length).toBe(0); }); }); + describe('includeRawScriptCoverage', function () { + it('should not include rawScriptCoverage field when disabled', async () => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); + await page.goto(server.PREFIX + '/jscoverage/simple.html', { + waitUntil: 'networkidle0', + }); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].rawScriptCoverage).toBeUndefined(); + }); + it('should include rawScriptCoverage field when enabled', async () => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage({ + includeRawScriptCoverage: true, + }); + await page.goto(server.PREFIX + '/jscoverage/simple.html', { + waitUntil: 'networkidle0', + }); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].rawScriptCoverage).toBeTruthy(); + }); + }); + // @see https://crbug.com/990945 + xit('should not hang when there is a debugger statement', async () => { + const { page, server } = getTestState(); + + await page.coverage.startJSCoverage(); + await page.goto(server.EMPTY_PAGE); + await page.evaluate(() => { + debugger; // eslint-disable-line no-debugger + }); + await page.coverage.stopJSCoverage(); + }); }); describeChromeOnly('CSSCoverage', function () {