ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Playwright Coverage API 深度解析:如何用 startJSCoverage 与 startCSSCoverage 采集页面覆盖率

Playwright Coverage API 深度解析:如何用 startJSCoverage 与 startCSSCoverage 采集页面覆盖率 Playwright Coverage API 深度解析如何用 startJSCoverage 与 startCSSCoverage 采集页面覆盖率【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwrightPlaywright 的CoverageAPI自 v1.11 起提供仅支持 JS 语言绑定用于收集页面实际使用过的 JavaScript 与 CSS 部分是构建真实用户视角覆盖率报告、Istanbul 报告以及 CSS 瘦身分析的核心能力。本文以 Coverage 类文档 为主体完整覆盖其全部方法与参数并结合仓库中的客户端实现 coverage.ts、Chromium 端实现 crCoverage.ts 及对应测试用例讲清每个参数的行为边界、底层 CDP 协议调用链以及常见的实战限制。一、Coverage 是什么适用前提是什么Coverage收集页面使用过的 JavaScript 和 CSS 的信息。它通过page.coverage属性挂载在 Page 对象上从源码结构看Page 类 声明了readonly coverage: Coverage并在构造时执行this.coverage new Coverage(this._channel)将底层通道句柄交给Coverage客户端封装。两条重要前提仅限 Chromium 内核浏览器。官方文档明确标注 Coverage APIs are only supported on Chromium-based browsers。Firefox 与 WebKit 下的 Page 没有可用的 coverage 后端调用会失败。采集窗口制。覆盖率不是常驻能力而是开始采集 → 操作页面 → 停止采集并取回数据的会话式流程对应四个 API方法作用返回值page.coverage.startJSCoverage(options?)开始 JS 覆盖率采集voidpage.coverage.stopJSCoverage()停止并取回 JS 覆盖率报告条目数组page.coverage.startCSSCoverage(options?)开始 CSS 覆盖率采集voidpage.coverage.stopCSSCoverage()停止并取回 CSS 覆盖率报告条目数组客户端封装非常薄四个方法都只是向 Page 通道转发请求且传入kNoTimeout表示这些调用不受默认超时限制见 coverage.ts。二、JavaScript 覆盖率startJSCoverage / stopJSCoverage2.1 文档示例生成 Istanbul 报告官方文档给出的完整用法是采集一次页面加载期间的 JS 覆盖情况再用v8-to-istanbul把 V8 格式转成 Istanbul 格式const { chromium } require(playwright); const v8toIstanbul require(v8-to-istanbul); (async () { const browser await chromium.launch(); const page await browser.newPage(); await page.coverage.startJSCoverage(); await page.goto(https://chromium.org); const coverage await page.coverage.stopJSCoverage(); for (const entry of coverage) { const converter v8toIstanbul(, 0, { source: entry.source }); await converter.load(); converter.applyCoverage(entry.functions); console.log(JSON.stringify(converter.toIstanbul())); } await browser.close(); })();示例中entry.source与entry.functions正是返回结构里的关键字段v8-to-istanbul消费的就是 V8 特有的覆盖格式。2.2 参数详解startJSCoverage接受两个选项均为可选参数类型默认值说明resetOnNavigationbooleantrue每次导航时是否重置已采集的覆盖率。注意文档标注该选项为 discouraged即使设为false覆盖率仍可能在导航时被重置这是浏览器架构限制所致reportAnonymousScriptsbooleanfalse是否报告页面动态生成的匿名脚本eval、new Function等没有 URL 的脚本。开启后匿名脚本的 URL 会显示为__playwright_evaluation_script__默认值在 crCoverage.ts 的JSCoverage.start中得到印证const { resetOnNavigation true, reportAnonymousScripts false } options;2.3 返回结构stopJSCoverage()返回所有脚本的覆盖率报告数组每个条目包含字段类型说明urlstring脚本 URLscriptIdstringCDP 脚本 IDsourcestring?脚本内容如能获取到functionsArrayV8 特有的覆盖格式数组每项含functionNamestring、isBlockCoverageboolean和ranges数组每项含count、startOffset、endOffset三个 int 字段一个关键行为细节默认不报告匿名脚本但带 sourceURL 的脚本会被报告。测试 js-coverage.spec.ts 验证了这一点——加载/jscoverage/sourceurl.html后返回条目的url是 sourceURL 声明的nicename.js而 js-coverage.spec.ts 中eval()脚本在默认配置下被忽略开启reportAnonymousScripts: true后则能拿到url: 、source: console.log(foo)的条目。2.4 底层原理CDP 协议调用链从 crCoverage.ts 的JSCoverage.start实现看开始 JS 采集实际做了四件事并同时订阅三个 CDP 事件await Promise.all([ this._client.send(Profiler.enable), this._client.send(Profiler.startPreciseCoverage, { callCount: true, detailed: true }), this._client.send(Debugger.enable), this._client.send(Debugger.setSkipAllPauses, { skip: true }) ]);Profiler.startPreciseCoverage开启精确覆盖采集callCount: true使得每个 range 带有执行计数即返回结构里的countdetailed: true提供函数级细分functions字段。Debugger.setSkipAllPauses直接解释了文档中一个容易踩的坑若页面代码里含有debugger语句覆盖率采集不会因此挂起。测试用例 js-coverage.spec.ts 专门验证了should not hang when there is a debugger statement源码中对应的_onDebuggerPaused还会兜底地调用Debugger.resume。Debugger.scriptParsed事件回调_onScriptParsed负责在脚本解析时记录 scriptId 并尽力通过Debugger.getScriptSource拉取源码文本导航已发生时该调用会失败并被忽略这解释了返回结构中source字段如能获取到if applicable的语义。停止采集时stop()并发执行Profiler.takePreciseCoverage、Profiler.stopPreciseCoverage、Profiler.disable、Debugger.disable然后做两层过滤只保留本次会话内见过的 scriptId若脚本没有 URL 且未开启reportAnonymousScripts直接丢弃——这与 2.2 节的匿名脚本规则完全对应。三、CSS 覆盖率startCSSCoverage / stopCSSCoverage3.1 参数与返回结构startCSSCoverage只接受一个选项参数类型默认值说明resetOnNavigationbooleantrue每次导航时是否重置 CSS 覆盖率stopCSSCoverage()返回所有样式表的覆盖报告数组每个条目包含字段类型说明urlstring样式表 URLtextstring?样式表内容如能获取到rangesArray被使用的样式表区间已排序且互不重叠。每项含start文本中的起始偏移闭区间和end结束偏移开区间文档对 CSS 覆盖有一个明确限制不包含没有 sourceURL 的动态注入 style 标签。仓库测试 css-coverage.spec.ts 的 should ignore injected stylesheets 验证了这一点——通过page.addStyleTag({ content: ... })注入的样式在覆盖率结果中完全不存在返回长度为 0。3.2 ranges 为什么已排序且互不重叠底层实现揭示了ranges字段的产出过程。CSSCoverage.start会启用DOM.enable、CSS.enable并调用CSS.startRuleUsageTracking见 crCoverage.ts停止时通过CSS.stopRuleUsageTracking拿到浏览器报告的规则使用情况。浏览器报告的原始区间是可嵌套的一条被使用的规则会同时命中它的外层作用域。仓库源码中的convertToDisjointRanges函数crCoverage.ts用括号序列排序 扫描线算法把嵌套区间交集化先为每个区间生成 start/end 两个端点并按偏序规则排序再顺序扫描维护一个命中计数栈只在栈顶计数大于 0 时把区间记为被使用最后过滤掉长度小于 1 的空区间。这就是文档承诺Ranges are sorted and non-overlapping的实现依据。测试也给出了直观的验证加载simple.html后ranges恰好是[{ start: 1, end: 22 }]切片出的文本正是被使用的规则div { color: green; }见 css-coverage.spec.ts。媒体查询场景下media.html的覆盖结果是两段互不重叠的区间[8,15]和[17,38]完全没有覆盖的样式表unused.css则返回空ranges数组但条目本身仍在——即未使用的样式表也会被列出只是区间为空。四、resetOnNavigation 的深层行为差异两个 API 的resetOnNavigation虽然同名行为却并不对称这是文档注释中差异最明显的一处CSS 覆盖率设为false可以可靠地跨导航累积。css-coverage.spec.ts 的 should report stylesheets across navigations 测试中startCSSCoverage({ resetOnNavigation: false })后连续导航两个页面最终仍然拿到了 2 条样式表记录。JS 覆盖率文档将resetOnNavigation: false标记为 discouraged说明passingfalsedoes not guarantee that coverage persists through navigations, due to browser architecture limitations。对照测试 js-coverage.spec.ts默认配置resetOnNavigation为true下导航后stopJSCoverage()返回 0 条记录。从源码看两者重置机制都监听Runtime.executionContextsCleared事件事件触发时若_resetOnNavigation为真则清空已记录的 scriptId / stylesheet URL 集合_onExecutionContextsCleared。JS 一侧的额外不确定性来自 V8 引擎本身在跨进程导航时的 profile 生命周期因此文档才会强调不保证。实操建议如果你需要整条用户旅程的累积 JS 覆盖率更稳妥的做法是在每个导航点之间分段调用stopJSCoverage/startJSCoverage并自行合并而 CSS 覆盖率则可以直接用resetOnNavigation: false一次采到底。五、调用链全景与资源清理机制一条完整的覆盖率调用会经过三层客户端Coverage 将请求转发到 Page 通道Dispatcher 层pageDispatcher.ts 的startJSCoverage/stopJSCoverage/startCSSCoverage/stopCSSCoverage把请求路由到具体引擎实现的CRCoverage并维护_jsCoverageActive/_cssCoverageActive两个活跃标志Chromium 实现CRCoverage 内部持有JSCoverage与CSSCoverage两个独立会话对象各自管理 CDP 事件的订阅与释放。有两个容易忽略的工程细节值得注意重复 start 会直接报错。JSCoverage.start与CSSCoverage.start开头都有assert(!this._enabled, JSCoverage is already enabled)即同一引擎会话上必须先 stop 再 start而不是静默重置。页面销毁时自动兜底清理。pageDispatcher.ts 的_onDispose会在页面关闭时检查活跃标志若 JS/CSS 覆盖率仍在采集中则主动调用对应的stop方法忽略异常确保 CDP 会话不会泄漏在半开状态。也就是说即使脚本在 stop 之前异常退出或页面被强制关闭浏览器侧的 Profiler / CSS tracking 状态也会被释放。另外从测试组织方式看js-coverage.spec.ts 首行有it.skip(({ trace }) trace on)——当测试运行开启 tracing 时JS 覆盖率测试会被整体跳过从源码结构看可以推断覆盖率采集与 tracing 对同一 CDP 会话存在互斥关系实际使用时若在同一页面同时开启 trace 与 coverage需留意这一潜在冲突。六、实战要点清单结合文档注释与仓库实现使用 Coverage API 时的注意事项可以归纳为只在 Chromium 上运行写跨浏览器代码时先做能力判断避免 Firefox / WebKit 下报错。JS 覆盖率默认排除匿名脚本eval、new Function产物。若你的页面大量使用动态脚本且需要纳入报告显式传reportAnonymousScripts: true带 sourceURL 的脚本则始终会被报告url字段取 sourceURL 值。source字段可能缺失。源码获取依赖Debugger.getScriptSource/CSS.getStyleSheetText页面已导航离开时会静默失败消费方要按可选字段处理。CSS 覆盖率只统计有 sourceURL 的样式表addStyleTag注入的匿名样式不计入做 CSS 瘦身分析时动态注入的样式需要另行审计。导航行为不对称JS 侧resetOnNavigation: false不保证累积CSS 侧可靠需要跨页累积 JS 数据时分段采集再合并。停止是取回数据的唯一途径所有覆盖数据都只在stop*Coverage()时汇总返回底层takePreciseCoverage/stopRuleUsageTracking的语义start 之后不 stop 就拿不到任何结果页面销毁前若不 stopDispatcher 层的兜底清理只会释放资源数据不会留给你。无超时保护客户端对四个方法均使用kNoTimeout采集窗口内的长任务不会被 Playwright 侧超时中断超时控制需要自己通过业务逻辑实现。参考路径API 文档docs/src/api/class-coverage.md客户端封装packages/playwright-core/src/client/coverage.tsChromium 端实现packages/playwright-core/src/server/chromium/crCoverage.tsDispatcher 路由packages/playwright-core/src/server/dispatchers/pageDispatcher.ts测试与素材tests/library/chromium/js-coverage.spec.ts、tests/library/chromium/css-coverage.spec.ts、tests/assets/jscoverage、tests/assets/csscoverage【免费下载链接】playwrightPlaywright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.项目地址: https://gitcode.com/GitHub_Trending/pl/playwright创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表