Plate 仓库测试覆盖优先级刷新实战:基于 lcov 评分的非 React 包测试排期方法论
【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate
本篇指南以 Plate(GitHub_Trending/pl/plate)仓库中docs/plans/2026-03-24-coverage-priority-refresh-post-batch.md为骨架,完整讲解该仓库如何在大批量测试补充(coverage batch)之后重新运行全仓库覆盖率、按“值得写单测的接缝文件(seam file)”重新打分,并据此产出下一批测试工作清单。读完你将掌握:如何用bun test --coverage生成 lcov 数据、如何设计"排除 React / 惩罚近期已扫包 / 优先确定性变换"的评分规则、如何用阈值分布与包级总量识别真正值得投入的测试目标,以及tag、toggle、slash-command、mention、tabbable、juice、docx、docx-io、emoji等包的具体排期建议。
一、为什么要做"覆盖优先级刷新"
Plate 是一个以packages/**/src/**为源码主体的 monorepo 富文本编辑器项目(工作区声明见 package.json),其测试工作从 2026 年 3 月中旬起被组织成一系列"coverage-priority map"计划文档,形成了一条可追溯的执行链:
- 2026-03-17-coverage-priority-map.md(起点基线)
- 2026-03-22-coverage-priority-map-post-yjs.md
- 2026-03-23-coverage-priority-map-post-package-sweep.md
- 2026-03-24-coverage-priority-refresh-post-batch.md(本文主题)
- 以及配套的 2026-03-24-coverage-priority-map-refresh-post-batch.md(本次刷新产出的优先级地图正文)
核心动机在于:覆盖率的"总量"会骗人。当一批测试刚被补齐后,如果直接按最新 lcov 数据重新排序,那些刚被扫过的包会因其覆盖缺口仍然明显而继续霸占榜单,导致真正从未被触碰的包永远排不上号。因此每次 batch 结束后都需要一次"刷新":重跑覆盖、重新打分、并显式惩罚近期已完成的工作,让优先级列表回到"按真实剩余价值排序"的状态。
从仓库脚本看,完整测试入口包括 package.json 中的test:coverage(bun test --coverage)、test:all(pnpm test && pnpm test:slow)以及 tooling/scripts/test-fast.mjs 所代表的快慢两条测试通道;测试运行的全局配置集中在 bunfig.toml(预加载tooling/config/bunTestSetup.ts、使用tooling/config/tsconfig.test.json、默认onlyFailures = true)。
二、本次刷新的目标、约束与产物
依据原计划文档,本次刷新的目标(Goal)是:
在最近的覆盖批次之后重新运行全新仓库覆盖率,并基于当前状态重新生成非 React 包的包级与文件级排名。
其约束(Constraints)有四条,这也是整个评分体系的设计底线:
- 排除
/react:所有含 React 的实现(组件、hooks、浏览器交互)不作为单测优先级对象,避免把"需要浏览器/交互测试"的代码强行塞进纯单测通道。 - 不做覆盖率虚荣指标(no coverage vanity):不为凑覆盖率而打分,只为"真正值得直接单测或编辑器契约测试(editor-contract testing)"的文件打分。
- 仅对值得测试的文件计分:评分对象必须是存在确定性逻辑、可被单元级或编辑器契约级测试验证的接缝。
- 与 3 月 17、22、23、24 日的既有地图同步:已完成的 sweep 不得被过度优先(即"近期完成惩罚")。
本次的预期产物(Outputs)为三类:
- 刷新后的 Markdown 优先级地图(对应 2026-03-24-coverage-priority-map-refresh-post-batch.md);
- 刷新后的包级 TSV 矩阵;
- 刷新后的文件级 TSV 矩阵(后两者作为该地图的完整数据附表被引用,供精确分诊使用)。
执行阶段(Phases)依次为:读取既往地图与评分形态 → 重跑仓库覆盖率并检查全新 lcov → 生成刷新的包级与文件级分数 → 按价值汇总下一批最优工作。
三、覆盖率重跑命令与本次实测数据
计划文档明确记录了本次重跑使用的完整命令:
bun test --coverage --coverage-reporter=lcov --coverage-dir=.coverage-repo-2026-03-24b --reporter=dots逐段解析如下:
| 参数 | 作用 |
|---|---|
bun test --coverage | 以 Bun 测试运行器执行全仓库测试并开启覆盖率采集(对应 package.json 的test:coverage脚本) |
--coverage-reporter=lcov | 输出 lcov 格式覆盖率文件,供后续解析与评分 |
--coverage-dir=.coverage-repo-2026-03-24b | 指定本次覆盖率的输出目录(b后缀表示这是当天第二次刷新批次,首次为2026-03-24a,见 2026-03-24-coverage-priority-map.md) |
--reporter=dots | 使用点阵式输出,减少全量跑批的终端噪音 |
本次实测结果:
2687 pass,0 fail,覆盖510个文件,耗时2.37s;- 作为对比,同一天稍早的首次刷新(a 批次)为
2643 pass、493个文件、2.45s(见 2026-03-24-coverage-priority-map.md),可见两次跑批之间又补充了一批测试,文件数增加了 17 个且全量通过。
需要说明的适用前提:该命令针对仓库当前(2026-03-24 时点)的测试体系,实际运行前需完成依赖安装(仓库使用 pnpm 9.15.0 与 bun 1.3.x,见 package.json 的packageManager与 engines 声明),且测试环境配置以 bunfig.toml 为准。
四、评分规则:什么文件值得排进下一批
地图正文将评分规则(Scoring Rules)概括为六条,这是整个方法论的灵魂:
- 评分范围:
packages/**/src/**,即只对包源码计分,不包含测试文件、构建产物与仓库根目录脚本。 - 直接归零:
/react路径、任何 import React 的文件、浏览器类包、测试文件、barrel(索引转发文件,如各包index.ts)、dist产物以及纯类型文件一律记0分。 - 高分类别:确定性变换(deterministic transforms)、查询函数(queries)、解析器/序列化器辅助函数、插件覆写(plugin overrides)、以及覆盖率低的小型纯工具函数。
- 近期完成惩罚:最近扫过的包被有意扣分,避免"已工作包持续挤占未触碰工作"。
- 数据类常量与薄缺口被压低:纯数据常量、以及仅剩零星缺口的文件排到后面。
- 不追覆盖率虚荣:评分依据是"接缝类型 + 未覆盖行数 + 覆盖率比值 + 近期完成批次 + 包级信号惩罚"等多维信号(见 2026-03-24-coverage-priority-map.md 的 scoring signals 描述),而不是单纯的总行覆盖率数字。
从源码结构可以印证这些高分类别的典型形态:例如 packages/mention/src/lib/getMentionOnSelectItem.ts 属于"选中项回调"这类确定性逻辑,packages/udecode/cmdk/src/internal/command-score.ts 是命令模糊匹配的纯评分算法,packages/juice/src/lib/JuicePlugin.ts 是插件覆写层——它们都符合"确定性、可单测、低覆盖"的高分特征。
五、本次榜单的阈值分布与包级总量
阈值分布(Threshold Counts)
刷新后的分数分布呈现典型的"长尾"形态,是判断下一批工作量的直接依据:
| 阈值 | 文件数 |
|---|---|
score >= 10 | 2 |
score >= 9 | 5 |
score >= 8 | 6 |
score >= 7 | 8 |
score >= 6 | 8 |
score >= 5 | 26 |
score >= 4 | 56 |
score >= 3 | 89 |
score >= 2 | 111 |
score >= 1 | 400 |
可以看出:>=5是 26 个文件、>=6是 8 个文件,但>=1多达 400 个——绝大多数文件只有 1~4 分的低价值分,这正说明评分的作用不是制造一份"全要测"清单,而是把注意力收敛到头部少数接缝。
包级总量(Raw Package Totals)
按包汇总的原始分数(score 为该包内文件得分之和,top file 为该包最高分文件):
| 排名 | 包 | 总分 | 最高单文件分 |
|---|---|---|---|
| 1 | docx | 35 | 5 |
| 2 | docx-io | 34 | 5 |
| 3 | core | 30 | 5 |
| 4 | emoji | 29 | 5 |
| 5 | basic-styles | 27 | 5 |
| 6 | slate | 24 | 4 |
| 7 | tag | 18 | 9 |
| 8 | dnd | 15 | 3 |
| 9 | list | 15 | 3 |
| 10 | list-classic | 14 | 4 |
这里有一个关键的方法论警示:docx、docx-io、core、emoji等包的总分最高,但它们的单文件最高分只有 5 分,属于"刚被扫过、剩余价值分散且杠杆较低"的残留;而tag包总分仅 18,却拥有 9 分的单文件——按价值而非按包总量排序,才是下一批工作的正确姿势。
六、Strong Take:先做未被触碰的 score≥7 接缝文件
本次刷新的核心结论非常明确:
诚实的下一步不是再来一次大包 sweep,而是先做那些从未被触碰、score≥7 的接缝文件。
据此给出的首轮工作清单(含分数):
tag(标签包)
- isEqualTags.ts — 9 分,标签相等性判定,确定性纯函数
- BaseTagPlugin.ts — 9 分,标签插件基类覆写
toggle(切换包)
- BaseTogglePlugin.ts — 10 分
slash-command(斜杠命令包)
- BaseSlashPlugin.ts — 9 分
mention(提及包)
- getMentionOnSelectItem.ts — 7 分
- BaseMentionPlugin.ts — 5 分
udecode/cmdk(命令面板算法)
- command-score.ts — 8 分
tabbable(Tab 焦点可达性包)
- BaseTabbablePlugin.ts — 10 分
juice(内联样式包)
- JuicePlugin.ts — 7 分
docx 二次回访(刚扫过,次优先)
- getDocxIndent.ts — 5 分
- getTextListStyleType.ts — 5 分
- isDocxContent.ts — 5 分
docx-io 二次回访
- document.template.ts — 5 分
- core.ts — 5 分
emoji 工具函数二次回访
- IndexSearch.ts — 5 分
- EmojiInlineLibrary.ts — 5 分
从仓库现状看,这些文件大多已具备同目录同名.spec.ts/.spec.tsx测试骨架(例如 packages/tag/src/lib/isEqualTags.spec.tsx、packages/tabbable/src/lib/BaseTabbablePlugin.spec.ts、packages/slash-command/src/lib/BaseSlashPlugin.spec.ts、packages/udecode/cmdk/src/internal/command-score.spec.ts、packages/emoji/src/lib/utils/IndexSearch/IndexSearch.spec.ts),说明这些包"值得测"的判定与仓库既有的测试组织方式(源码与 spec 同目录平铺)是一致的。
关于 docx/docx-io 的次序,地图给出的判断是:它们仍有确定性残留,但属于第二梯队——因为刚被扫过,剩余部分杠杆较低,应当放在 score≥7 的"处女地"文件之后。
七、最佳剩余文件明细(Best Remaining Files)
地图附带了按"包 / 分数 / 覆盖率 / 未覆盖行数"细化的最优剩余文件清单,可作为精确排期的操作表(节选):
| 文件 | 包 | 分数 | 覆盖率 | 未覆盖行数 |
|---|---|---|---|---|
| BaseTabbablePlugin.ts | tabbable | 10 | 0.0% | 46 |
| BaseTogglePlugin.ts | toggle | 10 | 0.0% | 46 |
| isEqualTags.ts | tag | 9 | 0.0% | 42 |
| BaseSlashPlugin.ts | slash-command | 9 | 0.0% | 32 |
| BaseTagPlugin.ts | tag | 9 | 0.0% | 30 |
| command-score.ts | udecode/cmdk | 8 | 0.0% | 129 |
| getMentionOnSelectItem.ts | mention | 7 | 10.7% | 25 |
| JuicePlugin.ts | juice | 7 | 0.0% | 19 |
| IndexSearch.ts | emoji | 5 | 0.0% | 74 |
| GridSection.ts | emoji | 5 | 0.0% | 63 |
| document.template.ts | docx-io | 5 | 0.0% | 47 |
| core.ts | docx-io | 5 | 0.0% | 46 |
| Grid.ts | emoji | 5 | 0.0% | 42 |
| EmojiInlineLibrary.ts | emoji | 5 | 0.0% | 40 |
| getDocxIndent.ts | docx | 5 | 0.0% | 34 |
值得注意的是:榜单前列文件几乎全部是0.0%覆盖率的"零覆盖接缝",而getMentionOnSelectItem.ts是唯一有部分覆盖(10.7%)的头部文件——这说明评分模型同时考虑了"接缝价值"与"缺口规模",并非单纯按覆盖率从低到高排序。
八、本期明确跳过的工作(What I Would Skip For Now)
为避免优先级表被"看似该测、实则低杠杆"的文件污染,本次刷新明确给出三条跳过原则:
selection包整体跳过:其主体仍是 DOM 内部机制(internal machinery),即使个别辅助函数技术上不含 React,也不值得挤占单测通道。- 近期已扫包的低信号残留跳过:
core、slate、table、list、list-classic、markdown、suggestion、autoformat、dnd、basic-styles这些包刚完成 sweep,剩余的多是薄缺口与低杠杆文件。 - UI-only 包按设计过滤:纯 UI 包被评分规则天然排除,属于设计使然,而非遗漏。
这三条与本章第四节"近期完成惩罚""数据类常量压低"的评分规则一一对应,构成闭环:跳过清单不是临时决定,而是评分规则自然推导的结果。
九、刷新产物的配套与同步基线
本次刷新并非孤立动作,其输入(Inputs)与输出需要与既有地图链对齐:
- 覆盖数据源:
lcov.info(输出于.coverage-repo-2026-03-24b/目录,对应命令中的--coverage-dir)。 - 约束输入:排除
/react、跳过浏览器与 UI-heavy 包、只对值得直接单测或编辑器契约测试的文件计分。 - 同步基线:3 月 17、22、23 日及更早的 3 月 24 日地图,加上其间已完成的所有覆盖工作;刷新时必须对照这些基线,确保"已完成的 sweep 不被过度优先"。
配套的完整数据以包级与文件级两张 TSV 矩阵(2026-03-24-coverage-priority-packages-refresh-post-batch.tsv与2026-03-24-coverage-priority-files-refresh-post-batch.tsv)为分诊依据,前者给出全包矩阵、后者给出全文件矩阵;在 2026-03-24-coverage-priority-map-refresh-post-batch.md 中可以看到:TSV 里的包矩阵与文件矩阵用于"精确分诊(exact triage)",而本文所依据的计划文档则记录其生成过程与结论摘要。
十、方法论总结:把"覆盖率地图"变成可持续的排期循环
回顾本次刷新,可以提炼出一套可在任何大型 TS/JS monorepo 复用的循环流程:
- 跑批:执行
bun test --coverage --coverage-reporter=lcov --coverage-dir=<批次目录> --reporter=dots,得到当下全仓库 lcov 数据(对应仓库 package.json 的test:coverage入口)。 - 打分:仅对
packages/**/src/**内的文件打分,排除/react、barrel、dist、纯类型与测试文件;高分为确定性变换、查询、解析/序列化辅助、插件覆写、小纯工具;对近期已扫包施加惩罚。 - 收敛:用阈值分布(score≥7 / ≥5 / ≥1 的文件数)判断下一批宽度,用包级总量 + 最高单文件分区分"总量大但低杠杆"与"单文件高分"两种包。
- 排序:先做未被触碰的 score≥7 接缝,其次才回访刚扫过的
docx/docx-io/emoji等残留;selection与 UI-only 包明确跳过。 - 固化:将结论落成新的 markdown 地图与 TSV 矩阵,作为下一轮刷新的同步基线,从而让优先级列表始终反映"当前真实剩余价值"而不是"谁的缺口大"。
这套方法的核心价值在于对抗"覆盖率虚荣":它不追求把 400 个 1 分文件全部补齐,而是保证每一轮测试投入都花在杠杆最高的确定性接缝上,并通过"惩罚近期完成项"让排行榜自动滚动,避免测试工作长期滞留于同一批包内。
【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考