feat: Implement JavaScript Coverage (#1673)
This patch introduces a new `page.coverage` namespace with two methods: - `page.coverage.startJSCoverage` to initiate JavaScript coverage recording - `page.coverage.stopJSCoverage` to stop JavaScript coverage and get results
This commit is contained in:
parent
bd73e4b7b8
commit
d062381978
44
docs/api.md
44
docs/api.md
@ -52,6 +52,7 @@
|
|||||||
* [page.close()](#pageclose)
|
* [page.close()](#pageclose)
|
||||||
* [page.content()](#pagecontent)
|
* [page.content()](#pagecontent)
|
||||||
* [page.cookies(...urls)](#pagecookiesurls)
|
* [page.cookies(...urls)](#pagecookiesurls)
|
||||||
|
* [page.coverage](#pagecoverage)
|
||||||
* [page.deleteCookie(...cookies)](#pagedeletecookiecookies)
|
* [page.deleteCookie(...cookies)](#pagedeletecookiecookies)
|
||||||
* [page.emulate(options)](#pageemulateoptions)
|
* [page.emulate(options)](#pageemulateoptions)
|
||||||
* [page.emulateMedia(mediaType)](#pageemulatemediamediatype)
|
* [page.emulateMedia(mediaType)](#pageemulatemediamediatype)
|
||||||
@ -197,6 +198,9 @@
|
|||||||
* [target.page()](#targetpage)
|
* [target.page()](#targetpage)
|
||||||
* [target.type()](#targettype)
|
* [target.type()](#targettype)
|
||||||
* [target.url()](#targeturl)
|
* [target.url()](#targeturl)
|
||||||
|
- [class: Coverage](#class-coverage)
|
||||||
|
* [coverage.startJSCoverage(options)](#coveragestartjscoverageoptions)
|
||||||
|
* [coverage.stopJSCoverage()](#coveragestopjscoverage)
|
||||||
|
|
||||||
<!-- tocstop -->
|
<!-- tocstop -->
|
||||||
|
|
||||||
@ -600,6 +604,10 @@ Gets the full HTML contents of the page, including the doctype.
|
|||||||
If no URLs are specified, this method returns cookies for the current page URL.
|
If no URLs are specified, this method returns cookies for the current page URL.
|
||||||
If URLs are specified, only cookies for those URLs are returned.
|
If URLs are specified, only cookies for those URLs are returned.
|
||||||
|
|
||||||
|
#### page.coverage
|
||||||
|
|
||||||
|
- returns: <[Coverage]>
|
||||||
|
|
||||||
#### page.deleteCookie(...cookies)
|
#### page.deleteCookie(...cookies)
|
||||||
- `...cookies` <...[Object]>
|
- `...cookies` <...[Object]>
|
||||||
- `name` <[string]> **required**
|
- `name` <[string]> **required**
|
||||||
@ -2144,6 +2152,41 @@ Identifies what kind of target this is. Can be `"page"`, `"service_worker"`, or
|
|||||||
#### target.url()
|
#### target.url()
|
||||||
- returns: <[string]>
|
- returns: <[string]>
|
||||||
|
|
||||||
|
### class: Coverage
|
||||||
|
|
||||||
|
#### coverage.startJSCoverage(options)
|
||||||
|
- `options` <[Object]> Set of configurable options for coverage
|
||||||
|
- `resetOnNavigation` <[boolean]> Whether to reset coverage on every navigation. Defaults to `true`.
|
||||||
|
- returns: <[Promise]> Promise that resolves when coverage is started
|
||||||
|
|
||||||
|
#### coverage.stopJSCoverage()
|
||||||
|
- returns: <[Promise]<[Array]<[Object]>>> Promise that resolves to the array of coverage reports for all non-anonymous scripts
|
||||||
|
- `url` <[string]> Script URL
|
||||||
|
- `text` <[string]> Script content
|
||||||
|
- `ranges` <[Array]<[Object]>> Script ranges that were executed. Ranges are sorted and non-overlapping.
|
||||||
|
- `start` <[number]> A start offset in text, inclusive
|
||||||
|
- `end` <[number]> An end offset in text, exclusive
|
||||||
|
|
||||||
|
> **NOTE** JavaScript Coverage doesn't include anonymous scripts; however, scripts with sourceURLs are
|
||||||
|
reported.
|
||||||
|
|
||||||
|
An example of using JavaScript coverage to get percentage of executed
|
||||||
|
code:
|
||||||
|
|
||||||
|
```js
|
||||||
|
await page.startJSCoverage();
|
||||||
|
await page.goto('https://example.com');
|
||||||
|
const coverage = await page.stopJSCoverage();
|
||||||
|
let totalBytes = 0;
|
||||||
|
let usedBytes = 0;
|
||||||
|
for (const entry of coverage) {
|
||||||
|
totalBytes += entry.text.length;
|
||||||
|
for (const range of entry.ranges)
|
||||||
|
usedBytes += range.end - range.start - 1;
|
||||||
|
}
|
||||||
|
console.log(`Coverage is ${usedBytes / totalBytes * 100}%`);
|
||||||
|
```
|
||||||
|
|
||||||
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
|
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
|
||||||
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
|
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
|
||||||
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
|
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
|
||||||
@ -2158,6 +2201,7 @@ Identifies what kind of target this is. Can be `"page"`, `"service_worker"`, or
|
|||||||
[Frame]: #class-frame "Frame"
|
[Frame]: #class-frame "Frame"
|
||||||
[ConsoleMessage]: #class-consolemessage "ConsoleMessage"
|
[ConsoleMessage]: #class-consolemessage "ConsoleMessage"
|
||||||
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
|
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
|
||||||
|
[Coverage]: #class-coverage "Coverage"
|
||||||
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
|
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
|
||||||
[Response]: #class-response "Response"
|
[Response]: #class-response "Response"
|
||||||
[Request]: #class-request "Request"
|
[Request]: #class-request "Request"
|
||||||
|
178
lib/Coverage.js
Normal file
178
lib/Coverage.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 {helper, debugError} = require('./helper');
|
||||||
|
|
||||||
|
class Coverage {
|
||||||
|
/**
|
||||||
|
* @param {!Puppeteer.Session} client
|
||||||
|
*/
|
||||||
|
constructor(client) {
|
||||||
|
this._jsCoverage = new JSCoverage(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!Object} options
|
||||||
|
*/
|
||||||
|
async startJSCoverage(options) {
|
||||||
|
return await this._jsCoverage.start(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopJSCoverage() {
|
||||||
|
return await this._jsCoverage.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {Coverage};
|
||||||
|
helper.tracePublicAPI(Coverage);
|
||||||
|
|
||||||
|
class JSCoverage {
|
||||||
|
/**
|
||||||
|
* @param {!Puppeteer.Session} client
|
||||||
|
*/
|
||||||
|
constructor(client) {
|
||||||
|
this._client = client;
|
||||||
|
this._enabled = false;
|
||||||
|
this._scriptURLs = new Map();
|
||||||
|
this._scriptSources = new Map();
|
||||||
|
this._eventListeners = [];
|
||||||
|
this._resetOnNavigation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!Object} options
|
||||||
|
*/
|
||||||
|
async start(options = {}) {
|
||||||
|
console.assert(!this._enabled, 'JSCoverage is already enabled');
|
||||||
|
this._resetOnNavigation = options.resetOnNavigation === undefined ? true : !!options.resetOnNavigation;
|
||||||
|
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 anonymous scripts
|
||||||
|
if (!event.url)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Promise<!Array<!{url:string, text:string, ranges:!Array<!{start:number, end:number}>}>>}
|
||||||
|
*/
|
||||||
|
async stop() {
|
||||||
|
console.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) {
|
||||||
|
const url = this._scriptURLs.get(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!Array<!{startOffset:number, endOffset:number, count:number}>} nestedRanges
|
||||||
|
* @return {!Array<!{start:number, end:number}>}
|
||||||
|
*/
|
||||||
|
function convertToDisjointRanges(nestedRanges) {
|
||||||
|
const points = [];
|
||||||
|
for (const range of nestedRanges) {
|
||||||
|
points.push({ offset: range.startOffset, type: 0, range });
|
||||||
|
points.push({ offset: range.endOffset, type: 1, range });
|
||||||
|
}
|
||||||
|
// Sort points to form a valid parenthesis sequence.
|
||||||
|
points.sort((a, b) => {
|
||||||
|
// Sort with increasing offsets.
|
||||||
|
if (a.offset !== b.offset)
|
||||||
|
return a.offset - b.offset;
|
||||||
|
// All "end" points should go before "start" points.
|
||||||
|
if (a.type !== b.type)
|
||||||
|
return b.type - a.type;
|
||||||
|
const aLength = a.range.endOffset - a.range.startOffset;
|
||||||
|
const bLength = b.range.endOffset - b.range.startOffset;
|
||||||
|
// For two "start" points, the one with longer range goes first.
|
||||||
|
if (a.type === 0)
|
||||||
|
return bLength - aLength;
|
||||||
|
// For two "end" points, the one with shorter range goes first.
|
||||||
|
return aLength - bLength;
|
||||||
|
});
|
||||||
|
|
||||||
|
const hitCountStack = [];
|
||||||
|
const results = [];
|
||||||
|
let lastOffset = 0;
|
||||||
|
// Run scanning line to intersect all ranges.
|
||||||
|
for (const point of points) {
|
||||||
|
if (hitCountStack.length && lastOffset < point.offset && hitCountStack[hitCountStack.length - 1] > 0) {
|
||||||
|
const lastResult = results.length ? results[results.length - 1] : null;
|
||||||
|
if (lastResult && lastResult.end === lastOffset)
|
||||||
|
lastResult.end = point.offset;
|
||||||
|
else
|
||||||
|
results.push({start: lastOffset, end: point.offset});
|
||||||
|
}
|
||||||
|
lastOffset = point.offset;
|
||||||
|
if (point.type === 0)
|
||||||
|
hitCountStack.push(point.range.count);
|
||||||
|
else
|
||||||
|
hitCountStack.pop();
|
||||||
|
}
|
||||||
|
// Filter out empty ranges.
|
||||||
|
return results.filter(range => range.end - range.start > 1);
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ const {FrameManager} = require('./FrameManager');
|
|||||||
const {Keyboard, Mouse, Touchscreen} = require('./Input');
|
const {Keyboard, Mouse, Touchscreen} = require('./Input');
|
||||||
const Tracing = require('./Tracing');
|
const Tracing = require('./Tracing');
|
||||||
const {helper, debugError} = require('./helper');
|
const {helper, debugError} = require('./helper');
|
||||||
|
const {Coverage} = require('./Coverage');
|
||||||
|
|
||||||
const writeFileAsync = helper.promisify(fs.writeFile);
|
const writeFileAsync = helper.promisify(fs.writeFile);
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ class Page extends EventEmitter {
|
|||||||
/** @type {!Map<string, Function>} */
|
/** @type {!Map<string, Function>} */
|
||||||
this._pageBindings = new Map();
|
this._pageBindings = new Map();
|
||||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
||||||
|
this._coverage = new Coverage(client);
|
||||||
|
|
||||||
this._screenshotTaskQueue = screenshotTaskQueue;
|
this._screenshotTaskQueue = screenshotTaskQueue;
|
||||||
|
|
||||||
@ -123,6 +125,13 @@ class Page extends EventEmitter {
|
|||||||
return this._touchscreen;
|
return this._touchscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {!Coverage}
|
||||||
|
*/
|
||||||
|
get coverage() {
|
||||||
|
return this._coverage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} selector
|
* @param {string} selector
|
||||||
*/
|
*/
|
||||||
|
15
test/assets/jscoverage/involved.html
Normal file
15
test/assets/jscoverage/involved.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<script>
|
||||||
|
function foo() {
|
||||||
|
if (1 > 2)
|
||||||
|
console.log(1);
|
||||||
|
if (1 < 2)
|
||||||
|
console.log(2);
|
||||||
|
let x = 1 > 2 ? 'foo' : 'bar';
|
||||||
|
let y = 1 < 2 ? 'foo' : 'bar';
|
||||||
|
let z = () => {};
|
||||||
|
let q = () => {};
|
||||||
|
q();
|
||||||
|
}
|
||||||
|
|
||||||
|
foo();
|
||||||
|
</script>
|
2
test/assets/jscoverage/multiple.html
Normal file
2
test/assets/jscoverage/multiple.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<script src='script1.js'></script>
|
||||||
|
<script src='script2.js'></script>
|
2
test/assets/jscoverage/ranges.html
Normal file
2
test/assets/jscoverage/ranges.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<script>
|
||||||
|
function unused(){}console.log('used!');</script>
|
1
test/assets/jscoverage/script1.js
Normal file
1
test/assets/jscoverage/script1.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
console.log(3);
|
1
test/assets/jscoverage/script2.js
Normal file
1
test/assets/jscoverage/script2.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
console.log(3);
|
2
test/assets/jscoverage/simple.html
Normal file
2
test/assets/jscoverage/simple.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<script>
|
||||||
|
function foo() {function bar() { } console.log(1); } foo(); </script>
|
4
test/assets/jscoverage/sourceurl.html
Normal file
4
test/assets/jscoverage/sourceurl.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<script>
|
||||||
|
console.log(1);
|
||||||
|
//# sourceURL=nicename.js
|
||||||
|
</script>
|
1
test/assets/jscoverage/unused.html
Normal file
1
test/assets/jscoverage/unused.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<script>function foo() { }</script>
|
28
test/golden/jscoverage-involved.txt
Normal file
28
test/golden/jscoverage-involved.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"url": "http://localhost:8907/jscoverage/involved.html",
|
||||||
|
"ranges": [
|
||||||
|
{
|
||||||
|
"start": 0,
|
||||||
|
"end": 35
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 50,
|
||||||
|
"end": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 107,
|
||||||
|
"end": 141
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 148,
|
||||||
|
"end": 160
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"start": 168,
|
||||||
|
"end": 207
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "\nfunction foo() {\n if (1 > 2)\n console.log(1);\n if (1 < 2)\n console.log(2);\n let x = 1 > 2 ? 'foo' : 'bar';\n let y = 1 < 2 ? 'foo' : 'bar';\n let z = () => {};\n let q = () => {};\n q();\n}\n\nfoo();\n"
|
||||||
|
}
|
||||||
|
]
|
78
test/test.js
78
test/test.js
@ -3411,6 +3411,84 @@ describe('Page', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('JSCoverage', function() {
|
||||||
|
it('should work', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage();
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/simple.html');
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(1);
|
||||||
|
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||||
|
expect(coverage[0].ranges).toEqual([
|
||||||
|
{ start: 0, end: 17 },
|
||||||
|
{ start: 35, end: 61 },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('should report sourceURLs', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage();
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/sourceurl.html');
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(1);
|
||||||
|
expect(coverage[0].url).toBe('nicename.js');
|
||||||
|
});
|
||||||
|
it('should ignore anonymous scripts', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage();
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.evaluate(() => console.log(1));
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(0);
|
||||||
|
});
|
||||||
|
it('should report multiple scripts', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage();
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(2);
|
||||||
|
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||||
|
expect(coverage[0].url).toContain('/jscoverage/script1.js');
|
||||||
|
expect(coverage[1].url).toContain('/jscoverage/script2.js');
|
||||||
|
});
|
||||||
|
it('should report right ranges', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage();
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/ranges.html');
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(1);
|
||||||
|
const entry = coverage[0];
|
||||||
|
expect(entry.ranges.length).toBe(1);
|
||||||
|
const range = entry.ranges[0];
|
||||||
|
expect(entry.text.substring(range.start, range.end)).toBe(`console.log('used!');`);
|
||||||
|
});
|
||||||
|
it('should report scripts that have no coverage', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage();
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/unused.html');
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(1);
|
||||||
|
const entry = coverage[0];
|
||||||
|
expect(entry.url).toContain('unused.html');
|
||||||
|
expect(entry.ranges.length).toBe(0);
|
||||||
|
});
|
||||||
|
it('should work with conditionals', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage();
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/involved.html');
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(JSON.stringify(coverage, null, 2)).toBeGolden('jscoverage-involved.txt');
|
||||||
|
});
|
||||||
|
describe('resetOnNavigation', function() {
|
||||||
|
it('should report scripts across navigations when disabled', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage({resetOnNavigation: false});
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(2);
|
||||||
|
});
|
||||||
|
it('should NOT report scripts across navigations when enabled', async function({page, server}) {
|
||||||
|
await page.coverage.startJSCoverage(); // Enabled by default.
|
||||||
|
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
const coverage = await page.coverage.stopJSCoverage();
|
||||||
|
expect(coverage.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.COVERAGE) {
|
if (process.env.COVERAGE) {
|
||||||
|
@ -24,6 +24,7 @@ const EXCLUDE_CLASSES = new Set([
|
|||||||
'Downloader',
|
'Downloader',
|
||||||
'EmulationManager',
|
'EmulationManager',
|
||||||
'FrameManager',
|
'FrameManager',
|
||||||
|
'JSCoverage',
|
||||||
'Helper',
|
'Helper',
|
||||||
'Launcher',
|
'Launcher',
|
||||||
'Multimap',
|
'Multimap',
|
||||||
|
Loading…
Reference in New Issue
Block a user