plate/code-drawing 非 React 覆盖测试void 节点、插入变换与四种图表渲染器的测试设计实践【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate本文以仓库内 code-drawing 覆盖通过计划 为骨架结合 platejs/code-drawing 包的源码与测试实现完整讲解一次小规模非 React 覆盖测试通过的技术路线如何围绕BaseCodeDrawingPlugin、insertCodeDrawing、renderers三个真实接缝seams设计测试、约束覆盖范围、并给出可复用的验证命令序列。读完你可以掌握 Plate 插件包中 void 节点声明、节点插入变换与渲染工具函数的测试写法以及如何在 plate 仓库中跑一次最小化、无react、无伪造冒烟覆盖的测试迭代。一、覆盖通过计划的技术定位该计划的定位非常明确只做一次小规模small的非 React 覆盖通过对象是platejs/code-drawing包。它把包的真实可测接缝收敛为三个BaseCodeDrawingPlugin—— 插件本体负责把 code drawing 节点声明为 void 元素insertCodeDrawing—— 插入变换transform负责向编辑器插入默认/自定义的节点形状renderers—— 渲染工具函数负责把 PlantUml、Graphviz、Flowchart、Mermaid 四种图表源码渲染为 data URL。计划同时给出两条硬性约束这是该文档最有工程指导价值的部分fast lane only仅走快车道只做最小必要改动不做大范围重构no/react明确跳过 packages/code-drawing/src/react 目录下的 React 组件如 CodeDrawingPlugin.tsx也不做浏览器下载管道downloadImage的测试除非直接渲染器测试暴露了真实问题no fake smoke coverage不做伪造冒烟覆盖不允许为了凑覆盖率而写空断言或假用例。也就是说这是一次测试先行、范围克制、证据充分的覆盖通过其产出就是三份 spec 文件与对应的验证命令。二、第一个接缝BaseCodeDrawingPlugin的 void 节点声明插件定义与节点模型BaseCodeDrawingPlugin.ts 中插件通过createSlatePlugin声明export const CODE_DRAWING_KEY code_drawing as const; export interface TCodeDrawingElement extends TElement { data?: CodeDrawingData; } export const BaseCodeDrawingPlugin createSlatePlugin({ key: CODE_DRAWING_KEY, node: { isElement: true, isVoid: true }, });关键点node.isElement: true表示该节点是元素节点element可携带子节点node.isVoid: true表示它是void 元素即内部不存放可编辑文本、不参与光标逐字移动的原子型节点类似于 image、mention。这也是计划中code drawing nodes are void elements要验证的核心契约。TCodeDrawingElement通过可选data字段携带CodeDrawingData。该数据结构定义在 types.tsexport type CodeDrawingData { drawingType?: CodeDrawingType; drawingMode?: ViewMode; code?: string; };CodeDrawingType与ViewMode来自 constants.ts分别对应四种图表类型PlantUml、Graphviz、Flowchart、Mermaid见CODE_DRAWING_TYPE/CODE_DRAWING_TYPE_ARRAY和三种视图模式Both、Code、Image见VIEW_MODE/VIEW_MODE_ARRAY。常量文件还定义了 UI 相关默认值DEFAULT_MIN_HEIGHT 300、RENDER_DEBOUNCE_DELAY 500渲染防抖毫秒数、DOWNLOAD_FILENAME code-drawing.png。对应测试验证 void 契约BaseCodeDrawingPlugin.spec.ts 用一个真实创建的 Slate 编辑器来断言插件元数据而不是 mock 掉插件对象describe(BaseCodeDrawingPlugin, () { it(configures code drawing as a void element node, () { const editor createSlateEditor({ plugins: [BaseCodeDrawingPlugin], } as any); const plugin editor.getPlugin({ key: CODE_DRAWING_KEY }); expect(plugin.node.isElement).toBe(true); expect(plugin.node.isVoid).toBe(true); }); });要点通过createSlateEditor({ plugins: [BaseCodeDrawingPlugin] })走真实插件装配路径通过editor.getPlugin({ key: CODE_DRAWING_KEY })取回注册后的插件配置断言node.isElement true与node.isVoid true把void 元素这一行为契约固定下来防止未来改动悄悄破坏选区、光标或序列化语义。三、第二个接缝insertCodeDrawing插入变换变换实现与默认形状insertCodeDrawing.ts 是包暴露的插入入口export const insertCodeDrawing ( editor: SlateEditor, props: NodePropsTCodeDrawingElement {}, options: InsertNodesOptions {} ): void { const safeProps props typeof props object ? props : {}; const { data: propsData, ...restProps } safeProps; editor.tf.insertNodesTCodeDrawingElement( { children: [{ text: }], type: editor.getType(CODE_DRAWING_KEY), data: { drawingType: Mermaid, drawingMode: Both, code: , ...(typeof propsData object propsData ! null ? propsData : {}), }, ...restProps, }, { nextBlock: true, ...options } ); };值得注意的实现细节默认节点形状drawingType默认Mermaid、drawingMode默认Both、code默认空字符串子节点为[{ text: }]类型尊重配置节点type不硬编码而是调用editor.getType(CODE_DRAWING_KEY)因此通过BaseCodeDrawingPlugin.configure({ node: { type: custom-code-drawing } })改过类型后插入的节点会自动使用自定义类型数据合并语义自定义props.data通过展开运算符覆盖默认值但未被覆盖的字段如默认drawingMode: Both得以保留健壮性对props做对象检查防null/非对象传入并把nextBlock: true作为插入选项默认值调用方传入的options可覆盖。对应测试默认形状与合并行为insertCodeDrawing.spec.ts 覆盖两条用例用例 1插入默认节点形状L10-L42。编辑器初始只有一个p段落文本hi光标在offset 2调用insertCodeDrawing(editor)后断言新节点type等于CODE_DRAWING_KEYdata为{ code: , drawingMode: Both, drawingType: Mermaid }节点插在原段落之后。用例 2合并自定义 data 并尊重配置的节点类型L44-L85。先通过BaseCodeDrawingPlugin.configure({ node: { type: custom-code-drawing } })重命名类型再以{ data: { code: graph TD; A--B, drawingType: Graphviz } }调用。断言结果中type为custom-code-drawingdata为{ code: graph TD; A--B, drawingMode: Both, drawingType: Graphviz }——即自定义字段覆盖默认值未提供的drawingMode仍保持Both与实现中的展开合并逻辑一一对应。这两条用例正好把计划中的三个断言目标全部落实默认形状、尊重配置类型、自定义 data 合并保底。四、第三个接缝renderers四种渲染器统一入口与前置校验renderers.ts 的renderCodeDrawing(type, content)是统一入口export async function renderCodeDrawing( type: CodeDrawingType, content: string ): Promisestring { if (!content || !content.trim()) { return ; } switch (type) { case PlantUml: return renderPlantUml(content); case Graphviz: return renderGraphviz(content); case Flowchart: return renderFlowchart(content); case Mermaid: return renderMermaid(content); default: throw new Error(Unsupported drawing type: ${type}); } }这里有两处契约空内容短路空白内容直接返回空字符串不发起任何网络请求或初始化类型分派与兜底按四种类型分派到对应渲染器未识别类型抛Unsupported drawing type: ${type}。所有渲染器最终都通过svgToDataUrldata:image/svgxml;base64,前缀见 L24-L26把 SVG 文本转成 data URL便于后续直接作为img源或交给downloadImage下载。PlantUml编码 远端拉取 SVGrenderPlantUml 先动态import(plantuml-encoder)用其encode编码内容再fetchPlantUml 公共服务器https://www.plantuml.com/plantuml/svg/${encoded}取回 SVG 文本。若response.ok为假则抛Failed to fetch PlantUml SVG。注意它依赖网络与window.btoa属于浏览器侧渲染路径。Mermaid一次性初始化 幂等渲染renderMermaid 使用模块级mermaidInitialized标志保证mermaid.default.initialize({ startOnLoad: false })只执行一次后续每次渲染直接调用mermaid.default.render(id, content)id由randomString(6, lowerCase)生成如mermaid-abc123避免 DOM 内 SVG id 冲突。渲染失败无svg返回时抛Mermaid rendering failed。Graphviz 与 Flowchart库内渲染 DOM 临时节点renderGraphviz 动态导入viz.js与其full.render模块并带有extensionless fallback先尝试viz.js/full.render.js失败则回退到viz.js/full.render随后new Viz({ Module, render })并以{ format: svg, engine: dot }调用renderString。renderFlowchart 动态导入flowchart.jsparse(content)后创建一个display: none的临时 div 挂到document.body调用chart.drawSVG(el)取el.innerHTML最后移除临时节点并转 data URL——保证测试中 DOM 不残留垃圾节点。对应测试mock 模块与全局环境后的断言renderers.spec.ts 是这份 spec 中最重的一份其做法是用mock.module替换mermaid、plantuml-encoder、flowchart.js、viz.js、viz.js/full.render五个模块L32-L58用mock替换globalThis.fetchL60-L70并在afterAll中恢复fetch、console.error与mock.restore()L91-L95。断言覆盖renderPlantUmlencode收到原始内容、fetch收到拼接好的 SVG URL、结果以data:image/svgxml;base64,开头L97-L107ok: false时抛出Failed to fetch PlantUml SVG且console.error被调用L109-L128renderMermaid连续两次渲染initialize恰好只调用 1 次且参数为{ startOnLoad: false }render调用 2 次id 形如mermaid-xxxxxx两次结果都是 data URLL137-L148renderCodeDrawing空白内容 解析为空字符串L152-L156未知类型抛Unsupported drawing type: NopeL158-L162renderGraphviz通过 extensionless fallback 路径成功渲染renderString收到{ engine: dot, format: svg }L164-L172renderFlowchartparse收到源码、drawSVG被调用、渲染后document.body子元素数量与渲染前一致临时节点被清理、结果以 data URL 开头L174-L182renderCodeDrawing 分派按Graphviz、Flowchart类型分别分派到对应渲染器并返回 data URLL184-L191。可以看到测试不仅验证了计划中列出的四条行为PlantUml 编码/拉取/data URL、Mermaid 单次初始化/data URL、空内容返回空串、未知类型抛错还额外验证了 Graphviz 的 fallback 导入路径与 Flowchart 的 DOM 清理属于源码级证据的深化。五、验证命令序列与 Deferred 边界推荐的验证命令计划给出可复现的验证命令序列在仓库根目录执行分为三个层次定向验证针对本次改动直接涉及的 spec 运行bun test packages/code-drawing/src慢用例与性能画像pnpm test:profile -- --top 20取最慢 20 条、pnpm test:slowest -- --top 20取超时最严重 20 条用于确认没有引入拖慢测试套件的用例pnpm test:profile -- --top 20 packages/code-drawing/src pnpm test:slowest -- --top 20 packages/code-drawing/src依赖与构建闭环先pnpm install保证依赖一致再构建与类型检查pnpm install pnpm turbo build --filter./packages/code-drawing pnpm turbo typecheck --filter./packages/code-drawing最后跑pnpm lint:fix统一代码风格。其中bun test与仓库当前的 Bun 测试栈一致可见 bunfig.toml 与 package.jsonpnpm turbo --filter精确限定构建/类型检查范围到本包避免全仓重跑。明确的 Deferred 边界计划刻意把以下内容排除在本次覆盖之外作为后续迭代的候选/reactReact 侧的 CodeDrawingPlugin.tsx 与组件渲染路径需要浏览器/DOM 环境本轮不做downloadImagedownload.ts 中的图片下载管道Image 加载 → canvas 绘制 → 触发a download点击依赖真实浏览器行为本轮不测Graphviz 与 Flowchart 渲染器内部实现细节本轮只测到它们经统一入口可正确产出 data URL不深入其库内部算法。这与fast lane only、no/react、no fake smoke coverage的约束一致——用最少的测试面锁定最关键的三个接缝把容易在 CI 中抖动或需要重环境支撑的部分留到专门迭代。六、可复用的测试设计要点从这次覆盖通过中可提炼出几条对其它 Plate 插件包同样适用的经验先列真实接缝再写测试计划开篇即声明BaseCodeDrawingPlugin、insertCodeDrawing、renderers三个 seams测试文件与接缝一一对应BaseCodeDrawingPlugin.spec.ts、transforms/insertCodeDrawing.spec.ts、utils/renderers.spec.ts避免为覆盖率而测。契约断言放在真实编辑器实例上void 元素属性、插入后的节点形状都通过createSlateEditoreditor.getPlugin/editor.children断言而不是 mock 掉 Slate 核心。外部副作用全部 mock且测试后恢复网络fetch、重依赖mermaid/plantuml-encoder/viz.js/flowchart.js统一 mockafterAll里恢复globalThis.fetch与console.error并mock.restore()保证测试隔离。空输入与非法输入必须覆盖空字符串/空白内容返回空串、未知类型抛错这两条属于渲染入口的守卫契约成本最低、价值最高。明确不做边界避免范围蔓延/react、浏览器下载管道、库内部实现被显式写进 Deferred让本次通过的验收标准清晰可判。如果你想在 plate 仓库内复现直接打开 code-drawing 覆盖通过计划对照 BaseCodeDrawingPlugin.spec.ts、insertCodeDrawing.spec.ts、renderers.spec.ts 三份 spec再按上文命令序列执行即可。【免费下载链接】plateRich-text editor with AI and shadcn/ui项目地址: https://gitcode.com/GitHub_Trending/pl/plate创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 SEO 优化官网定制响应式建站教育培训建站