feat(coverage): option for raw V8 script coverage (#6454)

This commit is contained in:
Yosuke Kurami 2021-09-16 03:54:24 +09:00 committed by GitHub
parent caa2b732fe
commit cb4470a6d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 66 additions and 6 deletions

View File

@ -45,6 +45,17 @@ export interface CoverageEntry {
ranges: Array<{ start: number; end: number }>; 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. * Set of configurable options for JS coverage.
* @public * @public
@ -58,6 +69,10 @@ export interface JSCoverageOptions {
* Whether anonymous scripts generated by the page should be reported. * Whether anonymous scripts generated by the page should be reported.
*/ */
reportAnonymousScripts?: boolean; 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. * JavaScript Coverage doesn't include anonymous scripts by default.
* However, scripts with sourceURLs are reported. * However, scripts with sourceURLs are reported.
*/ */
async stopJSCoverage(): Promise<CoverageEntry[]> { async stopJSCoverage(): Promise<JSCoverageEntry[]> {
return await this._jsCoverage.stop(); return await this._jsCoverage.stop();
} }
@ -181,6 +196,7 @@ export class JSCoverage {
_eventListeners: PuppeteerEventListener[] = []; _eventListeners: PuppeteerEventListener[] = [];
_resetOnNavigation = false; _resetOnNavigation = false;
_reportAnonymousScripts = false; _reportAnonymousScripts = false;
_includeRawScriptCoverage = false;
constructor(client: CDPSession) { constructor(client: CDPSession) {
this._client = client; this._client = client;
@ -190,13 +206,18 @@ export class JSCoverage {
options: { options: {
resetOnNavigation?: boolean; resetOnNavigation?: boolean;
reportAnonymousScripts?: boolean; reportAnonymousScripts?: boolean;
includeRawScriptCoverage?: boolean;
} = {} } = {}
): Promise<void> { ): Promise<void> {
assert(!this._enabled, 'JSCoverage is already enabled'); assert(!this._enabled, 'JSCoverage is already enabled');
const { resetOnNavigation = true, reportAnonymousScripts = false } = const {
options; resetOnNavigation = true,
reportAnonymousScripts = false,
includeRawScriptCoverage = false,
} = options;
this._resetOnNavigation = resetOnNavigation; this._resetOnNavigation = resetOnNavigation;
this._reportAnonymousScripts = reportAnonymousScripts; this._reportAnonymousScripts = reportAnonymousScripts;
this._includeRawScriptCoverage = includeRawScriptCoverage;
this._enabled = true; this._enabled = true;
this._scriptURLs.clear(); this._scriptURLs.clear();
this._scriptSources.clear(); this._scriptSources.clear();
@ -215,7 +236,7 @@ export class JSCoverage {
await Promise.all([ await Promise.all([
this._client.send('Profiler.enable'), this._client.send('Profiler.enable'),
this._client.send('Profiler.startPreciseCoverage', { this._client.send('Profiler.startPreciseCoverage', {
callCount: false, callCount: this._includeRawScriptCoverage,
detailed: true, detailed: true,
}), }),
this._client.send('Debugger.enable'), this._client.send('Debugger.enable'),
@ -248,7 +269,7 @@ export class JSCoverage {
} }
} }
async stop(): Promise<CoverageEntry[]> { async stop(): Promise<JSCoverageEntry[]> {
assert(this._enabled, 'JSCoverage is not enabled'); assert(this._enabled, 'JSCoverage is not enabled');
this._enabled = false; this._enabled = false;
@ -278,7 +299,11 @@ export class JSCoverage {
const flattenRanges = []; const flattenRanges = [];
for (const func of entry.functions) flattenRanges.push(...func.ranges); for (const func of entry.functions) flattenRanges.push(...func.ranges);
const ranges = convertToDisjointRanges(flattenRanges); 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; return coverage;
} }

View File

@ -157,6 +157,41 @@ describe('Coverage specs', function () {
expect(coverage.length).toBe(0); 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 () { describeChromeOnly('CSSCoverage', function () {