news 2026/9/15 12:57:37

Plate 项目 basic-styles 与 math 包非 React 测试覆盖实战:从测试计划到源码级验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Plate 项目 basic-styles 与 math 包非 React 测试覆盖实战:从测试计划到源码级验证

Plate 项目 basic-styles 与 math 包非 React 测试覆盖实战:从测试计划到源码级验证

【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate

导读

本文基于 Plate(platejs)仓库中的测试覆盖执行计划文档 2026-03-23-basic-styles-and-math-coverage-pass.md,完整还原一次针对@platejs/basic-styles@platejs/math两个非 React 包的定向测试覆盖行动:包括覆盖目标、被测 API 的源码实现原理、聚焦测试用例的断言逻辑,以及从bun testturbo build/typecheck/lint的完整验证链路。读完本文,你将掌握这两个包中 9 个核心函数与插件的职责边界与调用方式,并了解 Plate 仓库"测试覆盖计划 → 聚焦 spec → 多命令验证"的标准化执行流程,可直接复用到其他包的覆盖工作中。

一、背景:为什么做这次覆盖 Pass

在 Plate 仓库中,packages/basic-stylespackages/math是两个纯逻辑(non-React)功能包,分别承载段落级排版样式与 KaTeX 数学公式渲染。随着仓库整体转向bun test测试运行器,并推行"按包、按函数粒度补齐覆盖"的测试策略,2026-03-23 的这份计划文档定义了一次小而聚焦的覆盖行动:只针对两个包的非 React 部分补写精确到单个函数/插件的 spec,并明确延后/react子目录、字体插件整体扫尾与额外的 KaTeX 行为矩阵。

其核心策略值得注意:先圈定精确的 API 名单(Scope),再逐一对齐源码实现补 spec,最后用一组标准命令验证。这与仓库内其他覆盖计划(如 2026-03-23-core-coverage-pass.md、2026-03-23-combobox-coverage-pass.md)属于同一套执行模式。

二、basic-styles 包的覆盖范围与源码原理

本次为@platejs/basic-styles圈定 4 个被测目标,均位于 packages/basic-styles/src/lib(React 版本在 packages/basic-styles/src/react,不在本次范围)。

2.1setLineHeight:注入式块级行高变换

源码位于 setLineHeight.ts,其实现体现了 Plate 的"注入(inject)"架构:

export const setLineHeight = ( editor: SlateEditor, value: number, setNodesOptions?: SetNodesOptions ): void => { const { defaultNodeValue, nodeKey } = editor.getInjectProps(BaseLineHeightPlugin); const match = getInjectMatch( editor, editor.getPlugin({ key: KEYS.lineHeight }) ); if (value === defaultNodeValue) { editor.tf.unsetNodes(nodeKey!, { match, ...setNodesOptions }); } else { editor.tf.setNodes({ [nodeKey!]: value }, { match: match as any, ...setNodesOptions, }); } };

关键行为:

  • 通过editor.getInjectProps(BaseLineHeightPlugin)读取插件的defaultNodeValuenodeKey,实现"单一数据源";
  • 当传入值等于默认值(1.5)时走unsetNodes删除lineHeight属性,否则用setNodes写入——即"回到默认即清理",避免文档中堆积冗余属性。

对应 spec setLineHeight.spec.tsx 用jsxt构造编辑器状态,覆盖三个断言分支:

  1. 对匹配的p块设置lineHeight: 2后,节点类型保持p并新增属性;
  2. 已带lineHeight={2}的段落调用setLineHeight(editor, 1.5)后属性被移除(回到默认值即清理);
  3. h1非注入目标调用时不产生任何变更——验证了getInjectMatch的匹配边界。

2.2toUnitLess:单位剥离工具

源码位于 toUnitLess.ts,实现极简但边界清晰:

const digitRegex = /\d+/; // return '0' if value not valid export const toUnitLess = (value: string): string => { const match = digitRegex.exec(value); if (!match) return '0'; const num = Number(match[0]); if (value.endsWith('rem')) return (num * 16).toString(); return num.toString(); };
  • 提取字符串中第一段连续数字;
  • 无法匹配(如空串、auto)时返回'0'
  • rem单位按 16px 基准换算为像素数值(2rem → '32');
  • px或纯数字则直接剥离单位。

对应 toUnitLess.spec.ts 用三组用例锁定了"非法值归零、px/纯数字保数值、rem 乘 16"三个契约,这是后续行高 HTML 反序列化时把 CSS 字符串规范化为数值的前提。

2.3BaseLineHeightPlugin:块级注入 + HTML 反序列化

源码位于 BaseLineHeightPlugin.ts,通过createSlatePlugin声明:

  • key: KEYS.lineHeightinject.isBlock: truetargetPlugins: [KEYS.p]——只注入到段落块
  • nodeProps声明defaultNodeValue: 1.5nodeKey: 'lineHeight'
  • targetPluginToInject向目标插件注入 HTML deserializer:解析<p style="line-height: ...">element.style.lineHeight为节点属性;
  • 最后extendTransforms暴露绑定编辑器实例的editor.tf.lineHeight.setNodes(value, options)

其 spec BaseLineHeightPlugin.spec.ts 验证了三件事:插件暴露的注入契约(isBlocktargetPluginsnodeProps)正确;注入的 deserializer 能把style.lineHeight转成{[lineHeight type]: '2'};通过editor.tf.lineHeight.setNodes(2, { at: [] })应用、再传1.5清除,与setLineHeight共用同一变换。

2.4BaseFontColorPlugin:叶子标记 + 颜色反序列化

源码位于 BaseFontColorPlugin.ts:

  • nodeProps声明defaultNodeValue: 'black'nodeKey: 'color'
  • HTML deserializer 标记isLeaf: truevalidStyle: { color: '*' },命中任意颜色样式时把element.style.color解析为叶子 mark;
  • extendTransforms暴露editor.tf.color.addMark(value),内部转发到editor.tf.addMarks({ [KEYS.color]: value })

其 spec BaseFontColorPlugin.spec.ts 验证了rgb(255, 0, 0)这类颜色字符串会被原样保留为KEYS.colormark,并确认addMark正确转发到编辑器级addMarks。这保证了从 HTML 粘贴带色文本时 mark 结构一致。

三、math 包的覆盖范围与源码原理

@platejs/math在 packages/math/src/lib 下实现公式节点模型与 KaTeX 渲染入口,React 交互层(packages/math/src/react)本次明确延后。被测目标共 5 个。

3.1insertEquationinsertInlineEquation:公式节点插入

两个变换均位于 packages/math/src/lib/transforms:

// insertEquation.ts editor.tf.insertNodes<TEquationElement>({ children: [{ text: '' }], texExpression: '', type: editor.getType(KEYS.equation), }, options);
// insertInlineEquation.ts editor.tf.insertNodes<TEquationElement>({ children: [{ text: '' }], texExpression: texExpression ?? editor.api.string(editor.selection), type: editor.getType(KEYS.inlineEquation), }, options);

差异点:块级insertEquation插入空表达式节点;行内insertInlineEquation若未显式传入texExpression,会用editor.api.string(editor.selection)把当前选区文本作为初始 TeX 表达式——这是"选中文本一键转行内公式"的典型入口。两个 spec(insertEquation.spec.ts、insertInlineEquation.spec.ts)分别断言了空表达式插入与选区文本回填两种路径。

3.2BaseEquationPluginBaseInlineEquationPlugin:节点类型与 HTML 导入

两个插件(BaseEquationPlugin.ts、BaseInlineEquationPlugin.ts)结构一致,差别在节点形态:

  • 块级:node: { isElement: true, isVoid: true }
  • 行内:node: { isElement: true, isInline: true, isVoid: true }
  • 两者 HTML deserializer 都从data-slate-tex-expression属性还原texExpression(用于复制/粘贴场景);
  • 均通过.overrideEditor(withEquation)挂载withEquation.internal.ts中的编辑器覆写逻辑,并通过extendEditorTransforms暴露editor.tf.insert.equation(...)editor.tf.insert.inlineEquation(...)
  • 块级插件额外import 'katex/dist/katex.min.css',确保公式渲染样式随包自带。

withEquation的底层公式归一化逻辑见 withEquation.internal.ts,输入规则($$等触发符)见 MathRules.ts 与 inputRules.spec.tsx。

3.3getEquationHtml:KaTeX 服务端渲染出口

源码位于 getEquationHtml.ts:

export const getEquationHtml = ({ element, options, }: { element: TEquationElement; options?: KatexOptions; }) => katex.renderToString(getEquationExpression(element), options);
  • 直接调用 KaTeX 的renderToString生成 HTML 字符串,可用于 SSR、导出或非浏览器环境;
  • 表达式经 getEquationExpression.internal.ts 归一化——该工具兼容早期 HTML 导入把数值/布尔属性存为原始类型的历史数据,统一String(...)转换,无法识别时回退空串;
  • options可透传KatexOptions(如displayModethrowOnError),getEquationHtml.spec.ts 覆盖了表达式归一化与渲染调用。

四、验证计划:多命令组合的完整链路

原计划文档给出的验证步骤,是本次覆盖"通过"判定的唯一依据,逐条如下:

命令作用
bun test(针对改动文件)快速验证本次新加的聚焦 spec
bun test packages/basic-styles/src/lib packages/math/src/lib对两个包的全部 lib 目录做完整回归
pnpm test:profile -- --top 20 packages/basic-styles/src packages/math/src输出两个包最慢的 20 个用例,检查性能回归
pnpm test:slowest -- --top 20 packages/basic-styles/src packages/math/src聚焦最慢用例清单,识别拖慢 CI 的测试
pnpm install重新解析依赖锁文件,确保新 spec 依赖可用
pnpm turbo build --filter=./packages/basic-styles --filter=./packages/math构建两个目标包(Turbo 增量构建)
pnpm turbo typecheck --filter=./packages/basic-styles --filter=./packages/math包级 TypeScript 类型检查
pnpm lint:fix修复 lint 问题,保持代码风格一致

这套组合覆盖了"测试运行 → 性能/慢用例体检 → 依赖解析 → 构建 → 类型 → 风格"六个维度,--filter=./packages/<name>把验证精确限制在改动包内,避免全仓构建拖慢反馈速度——这也是仓库内其他覆盖 pass 计划(如 2026-03-23-code-block-coverage-pass.md)共用的验证模板。

五、执行结果与覆盖收获

计划文档最终标记为status: completed,Result 确认:

  • setLineHeighttoUnitLessBaseLineHeightPluginBaseFontColorPlugin补写了聚焦 spec;
  • insertEquationinsertInlineEquationBaseEquationPluginBaseInlineEquationPlugingetEquationHtml补写了聚焦 spec;
  • 上述全部验证命令通过,包括定向bun test、lib 目录全量bun testtest:profile/test:slowest体检、pnpm install、turbo build、turbo typecheck 与lint:fix

六、延后项与后续建议

原计划明确声明本次不包含的内容,可作为后续覆盖工作的候选清单:

  • /react子目录(如 packages/math/src/react 下的useEquationInputEquationPlugin等,相关交互 spec 已存在于 useEquationInput.spec.tsx);
  • basic-styles 的宽泛字体插件整体扫尾(BaseFontFamilyPluginBaseFontSizePluginBaseFontWeightPluginBaseFontBackgroundColorPlugin等仍有独立 spec,可另行批量评估);
  • 额外的 KaTeX 行为矩阵(如错误表达式、不同KatexOptions组合的渲染快照)。

若沿用本计划的方法论,可将pnpm test:profile/test:slowest的输出作为后续性能优化(见 editor-performance-master-plan.md)的输入,形成"覆盖 → 体检 → 优化"闭环。

【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 12:56:43

批量下载抖音音频:douyin-downloader 上手指南

批量下载抖音音频&#xff1a;douyin-downloader 上手指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback support. 抖音…

作者头像 李华
网站建设 2026/9/15 12:52:26

STM32通过ESP8266接入阿里云MQTT上传数据

简介&#xff1a;面向STM32F103ZET6开发者、具备一定C语言基础的物联网实战资料&#xff0c;演示借助ESP8266无线模块将DHT11温湿度数据上传至阿里云平台。资源基于正点原子ZET6开发板&#xff0c;提供实物展示、完整接线方式说明、固件烧录指引以及常见问题汇总&#xff0c;适…

作者头像 李华
网站建设 2026/9/15 12:49:17

Flutter Web刷新白屏?路由404与Service Worker缓存排查指南

聊个比较有意思的 Flutter Web 问题。上周帮一个朋友排查他刚上线的 Flutter Web 后台系统&#xff0c;用户反馈说“从列表页点进详情&#xff0c;一切正常&#xff1b;但只要手一抖按了 F5&#xff0c;页面就白屏&#xff0c;浏览器地址栏那串路径还是原来的&#xff0c;后端日…

作者头像 李华