在富文本编辑器开发里,测试代码块这玩意儿看着不起眼,实际是验证编辑器质量最直接的手段。我早期做过一个跨平台文档系统,起初只用随手打的几段文字做验证,结果等到要集成时,内容渲染、序列化、剪贴板兼容各种问题集中爆发,白白折腾了两周。后来我重构了一套专门的测试代码块,把不同格式、边界字符、嵌套结构、异常内容都提前编排进去,再迭代新功能时就踏实多了。
这套东西能干什么?简单说,就是在一份内容里同时覆盖富文本编辑器的所有核心路径:样式解析、块级元素处理、列表嵌套、表格渲染、代码高亮、超链接跳转、特殊字符转义、长内容性能、空内容容错、跨平台粘贴兼容。适合谁看?正在做富文本编辑器、内容管理系统、在线文档、笔记工具,或者需要做编辑器选型和对比验收的开发者。即使你不做编辑器本身,只是要在自己的产品里嵌入一个编辑器,这份测试代码块也能帮你快速判断方案的成熟度。
整个验证思路其实很朴素:把你能想到的所有输入场景,集中塞进一份内容里,让编辑器在同一个样本上过一遍,问题和差异自然就暴露出来了。
1. 为什么需要专门的富文本测试代码块
1.1 富文本编辑器的“隐性雷区”
很多人觉得富文本编辑器不难做,无非就是一个可编辑的div加几个工具按钮。真有这么简单,市面上就不会有那么多编辑器内核轮子了。真正麻烦的地方在于,富文本内容在“编辑区域内部的实时DOM”和“保存到后端的结构化数据”之间来回转换,这两者不是天然一致的。
以标题为例,编辑器内部可能是<h2>标签,但保存时可能转成{"type":"header","level":2}这样的JSON结构。如果编辑器对某个块级节点处理得不干净,插入一个列表后再插入引用块,DOM层级就乱套了。类似的问题包括:连续空行的处理、列表嵌套层级、表格内的块级元素、加粗与斜体交叠的顺序、从外部复制HTML时夹带的标签污染。这些问题在简单内容测试下完全看不出来,只有用结构足够复杂的测试代码块,才能逼着它们在同一个样本上现形。
另外,富文本还有一个特殊性——同一个内容,在不同浏览器里的表现细节不同。Chrome的contenteditable行为相对规整,但Safari对部分选区操作、Firefox对部分粘贴格式化处理都有各自的脾气。浏览器差异会让同一个测试代码块的解析结果在不同环境里走向不同的分支,这正是需要一份统一测试样本的原因:只有同样的内容,才能横向对比各端的行为差异。
1.2 测试代码块应该覆盖哪几类内容
我设计的测试代码块,按内容类型划分,主要覆盖以下场景:
| 覆盖类型 | 具体内容举例 | 主要验证目标 |
|---|---|---|
| 基础行内样式 | 加粗、斜体、下划线、删除线、行内代码、上下标、字体颜色与背景色组合 | 行内标记解析、嵌套样式顺序 |
| 块级元素 | 标题层级H1-H4、普通段落、引用块、分割线、代码块 | 块级节点识别、区块边界处理 |
| 列表体系 | 有序列表、无序列表、任务列表、两层与三层嵌套、列表与段落混排 | 列表缩进、多级层级、类型切换 |
| 表格内容 | 简单表格、表头行、合并单元格效果(如支持)、单元格内嵌行内样式和块级元素 | 表格模型、单元格内HTML结构 |
| 链接与媒体 | 普通超链接、带标题属性的链接、图片、引用链接 | 链接序列化、资源地址转义 |
| 特殊字符 | HTML实体、Unicode特殊符号、emoji、全角标点、零宽字符、连续空格 | 转义机制、编码往返一致性 |
| 边界内容 | 超长无空格字符串、空段落、连续空行、仅空白字符的段落、空表格 | 容错能力、性能表现、DOM结构稳定性 |
| 嵌套组合 | 引用块内嵌列表、列表内嵌代码块、表格内嵌引用、多层嵌套引用 | 复杂嵌套层级解析、嵌套边界判定 |
这份表不是一次性想出来的,而是从真实项目里反向总结的。最初测试代码块只有前四类,后来因为连续出现了几次编辑器在粘贴外部内容后结构错乱的问题,才逐步补充了特殊字符和嵌套组合。
提示:设计测试代码块的核心原则是“宁滥勿缺”。一份内容里多放一个边界情况,就可能提前拦截一个线上才暴露的bug。内容冗余一点没关系,测试样本不追求精简。
2. 设计一份能打硬仗的测试文档
2.1 核心内容结构设计
测试文档的结构设计应该遵循“由表及里、由简到繁”的思路。先铺设基础的页面骨架,确保编辑器对常规元素支持正常,再逐步深入复杂嵌套和边界情况。我习惯把测试文档分成三个区块:基础区、嵌套区、压力区。
基础区包含一个H1标题、一段正文、一张两列三行的表格、一组有序和无序列表。这个区域主要验证编辑器对常规文档结构的支持程度。正文部分建议用一段包含中英文、数字、标点的混合文本,因为很多编辑器在中英文混排的行高、断行处理上会出现细微差异。
嵌套区是本套测试代码块的核心。引用块里嵌套一个有序列表、有序列表里再嵌套一个无序子列表、表格中的一个单元格包含一个引用块和一段代码,这些结构往往能直接暴露编辑器对“块元素的嵌套边界”处理不当的问题。我在项目中遇到过一种情况:在一个引用块里插入一个有序列表,保存后重新打开,列表的序号全部消失,界面显示成了普通段落。排查后才定位到,编辑器的序列化逻辑只处理了顶层块元素,没有递归处理引用块内部的嵌套列表。
压力区放置超长单词、连续空行、空段落、特殊Unicode字符以及复制自外部网页的富文本内容。这个区域主要用于性能测试和容错测试,观察编辑器在渲染大量复杂内容时的滚动流畅度、保存时序列化的耗时、以及解析异常内容时是否出现白屏或卡死。
2.2 特殊字符与边界情况怎么编排
特殊字符部分是最容易偷懒,也是回报最明显的区域。很多编辑器在测试报告里写着“支持特殊字符”,但实际测试时用的是普通键盘能打出来的那些符号。真正的边界测试,需要把这些内容编排进去:
- HTML实体类字符:
<、&、"、©。验证编辑器序列化时是否会二次转义,或者反过来,是否连用户手动输入的&都转义成&导致显示异常。 - 零宽字符与隐形字符:
\u200B(零宽空格)、\u2060(文字连接符)、\uFEFF(字节顺序标记)。这些字符粘贴进编辑器后肉眼不可见,但会残留在内容数据里,导致两个文本看似相同但哈希值不同,影响文档对比和搜索功能。 - 组合用变音符:比如
a\u0300这种由基础字母加组合音符构成的字符。部分编辑器在光标定位和字符计数时会把一个组合字符拆成两个码点,导致统计结果出错。 - 单向字符:希伯来语、阿拉伯语这类从右往左书写的文字。如果编辑器不支持双向文本算法,中英文与这些文字混排时会呈现错乱的视觉顺序。
- 连续空格与制表符:连续多个普通空格会被HTML渲染机制折叠成一个,除非内容里使用的是
或CSSwhite-space属性。测试时要把连续空格、制表符、换行符混在段落里,观察编辑器的保留策略。
把这些字符分散编排在基础区和压力区中,而不是单独列成一个特殊字符段落。因为独立成段时,编辑器可以针对性地处理,一旦混排进正常段落,处理逻辑才是真实用户会遇到的情况。
2.3 多层嵌套与复杂样式组合
嵌套是富文本编辑器测试中问题率最高的区域。常见的编辑器都是基于扁平块结构设计的,遇到一层嵌套还能应对,两层、三层嵌套就容易出状况。
我设计的嵌套样本之一是“引用块内嵌两层有序列表,第二层列表的一项包含一个加粗的斜体行内代码,并且加粗样式跨了两个列表项内容”。这种组合在DOM里表现为:
<blockquote> <ol> <li>第一层列表项A <ol> <li><strong><em><code>加粗斜体代码</code></em></strong></li> <li>第二层列表项B</li> </ol> </li> <li>第一层列表项B</li> </ol> </blockquote>这类结构考验的是编辑器对“行内样式跨界”和“列表层级变换”的组合处理能力。加粗标记如果从列表项A延伸到列表项B,保存后再加载时,标记可能被分裂成错误的片段,导致后续文字全部变粗。
另一个常用的组合是“表格单元格内嵌引用块,引用块内再嵌入一段代码”。不少编辑器允许用户在表格单元格里输入普通文本,但对单元格内的块级结构支持很敷衍,要么保存时静默丢弃,要么重新打开时结构断裂。这种场景用普通的测试文本根本测不出来,必须写入测试代码块才能触发。
3. 实操:从零搭一套测试代码块
3.1 准备工作与环境选择
在搭测试代码块之前,先要把测试环境固定下来。我通常会在本地准备一个最小可运行的前端工程,只包含编辑器组件和几个辅助脚本,不接后端存储。这样可以排除网络、接口、缓存等干扰因素,专心验证编辑器本身的行为。
环境方面建议覆盖这个组合:
| 环境维度 | 选择建议 | 原因 |
|---|---|---|
| 操作系统 | Windows、macOS、Linux各准备一台 | 字体渲染和剪切板行为有差异 |
| 浏览器 | Chrome、Edge、Firefox、Safari的最近两个大版本 | 覆盖主流引擎:Blink、WebKit、Gecko |
| 屏幕尺寸 | 桌面1920和1366、移动端375和414 | 布局变化会暴露一些隐藏的响应式问题 |
| 输入设备 | 键盘快捷键操作和鼠标点击操作交替进行 | 部分编辑器只对鼠标事件做了兼容,键盘操作会出现焦点丢失问题 |
测试数据的导入方式也建议提前确定。最稳妥的方式是准备一份包含全部标记的HTML文件,通过编辑器的“导入HTML”接口直接把内容注入。如果编辑器不提供导入接口,可以打开HTML文件后全选复制,再粘贴到编辑器里。这两种方式验证的目标不同:前者验证API层的数据解析,后者验证剪贴板的内容清洗逻辑,实际操作时建议两种都跑一遍。
3.2 分步写入测试内容
直接一次性把整个测试代码块粘贴进编辑器,可能会掩盖部分初始化问题。更稳妥的是分步写入,每写完一个区块就检查一次编辑器内部状态。我的操作步骤如下:
先用编辑器自带的功能逐项构建基础区内容,从标题开始,依次插入正文、表格和列表。在这一步,主要观察工具栏按钮是否都能正确激活选中元素的对应状态,比如光标落到H2标题上时,标题下拉框是否同步显示为H2。
接着写入嵌套区内容。嵌套区块建议通过两种方式写入:先用工具栏手工构造,再复制已构造的HTML片段在另一个段落粘贴一份。手工构造能验证编辑器的交互路径,粘贴构造能验证数据解析路径。两边的结果经常不一致,手工构造时编辑器会自己调整DOM结构,粘贴外部HTML时可能带进来非预期的标签。
最后注入压力区内容。这些内容建议单独准备一个文本文件,通过“全选复制再粘贴”的方式注入。注意观察粘贴后的即时行为:内容是否立即生效、光标位置是否正确、有没有出现延迟或卡顿。
分步写入还有一个好处,就是可以把每步之后编辑器的内部HTML导出保存。如果后面出现问题,可以通过对比这一步和最终结果的HTML差异,快速定位是哪一步导致的。
3.3 如何记录和验证测试结果
测试代码块跑完之后,关键工作在于验证结果。很多人测试时只看“界面显示正不正常”,这是远远不够的。我习惯从四个维度分别记录:
渲染一致性:把测试内容分别通过导入、粘贴、逐字输入三种方式放入编辑器,对比渲染结果是否一致。三种方式经常出现不同的结果。特别是表格内的换行和列表项的缩进,粘贴方式很容易把外部源里的冗余样式带进来,导致视觉表现偏离预期。
序列化往返一致性:把编辑器内容导出为最终存储格式(可能是纯HTML、Markdown或JSON),然后再重新导入编辑器,观察是否出现内容损失。我记录的项目里,最常见的问题是任务框符(如☐/☑)在导出再导入后丢失勾选状态,以及列表的起始序号start属性被重置。
复制粘贴兼容性:从编辑器复制一段内容,粘贴到富客户端(如Word或在线协作文档)中的效果,以及反过来从这些工具复制内容粘贴进编辑器的效果。这个场景最容易暴露问题,因为外部来源内容的标签规范与编辑器内部结构完全是两码事。Word的粘贴内容带有大量<o:p>标签和命名空间,处理不干净就会在保存后残留垃圾代码。
XSS与数据安全:在测试内容中加入<script>标签、带有javascript:协议的链接、onerror事件属性,观察编辑器是过滤掉,还是原样保留。原样保留意味着恶意用户可以通过编辑内容注入脚本,这是内容系统最不可接受的安全漏洞之一。这一项验证结果建议作为测试报告的第一优先级指标。
验证结果用一份简单的表格记录即可。每条检查项标记“通过/失败/存疑”,存疑项附带截图和当时执行的操作路径。记录的意义不只是当下排查,更重要的是,后续每次编辑器内核升级,都能拿这套结果对比回归。
4. 常见问题与排查技巧实录
4.1 内容“悄悄变样”的坑
测试过程中最让人头痛的问题,不是功能没有实现,而是内容保存后重新打开,不声不响地变了样子。表现形式很隐蔽:列表变成普通段落、链接文字丢失、代码块的换行变成空格,但用户很难说清楚是何时发生的。
这类问题的排查思路,是确认问题的触发点到底在“编辑阶段”还是“解析阶段”。我遇到过一回,内容里某段文字明明设置了红色,界面显示也正常,但导出数据里对应的颜色值就是没有出现。排查后发现是快捷键触发的Mark标签与工具栏设置的span style走了两个不同的生成路径,序列化时只识别了其中一种。
排查这类问题,建议在编辑器内部切入监听,在选区变化、内容变化、粘贴事件触发时分别导出当前的HTML快照,对比这些快照的差异,就能定位是哪一步把内容改掉了。单纯靠注视界面显示状态很难发现这类问题,因为很多DOM层面的变化在视觉上并无明显区分。
4.2 复制粘贴与剪贴板兼容
跨端复制粘贴是富文本编辑器测试的重灾区。从网站复制一段内容粘贴进编辑器,通常不只是文本本身,还带着源页面的一大堆样式声明、类名、事件属性。编辑器缺少清洗能力时,这些垃圾内容会被一并存入内容数据,导致同一个文档在另一处渲染时样式冲突。
处理思路一般分两层。第一层是粘贴入口的清洗,把HTML字符串经过白名单过滤,只保留编辑器自定义标签和有限的几种标准标签,其余全部剥除。第二层是粘贴后的规范化,把清洗后仍然存在的冗余标签按照编辑器的内容模型重新组织,比如把多个连续的<span>合并、把空标签删除、把不规范的嵌套结构修正。
实测中有一类比较难处理的情况是:从Word复制的表格粘贴进编辑器后,表格的宽度、边框、合并单元格信息全部丢失,只剩纯文本内容。这类问题与编辑器对表格节点的识别策略有关,一般需要专门针对<table>标签做转换处理。在测试代码块中加入从Word复制过来的表格样例,能直接验证编辑器在这条路径上的完善程度。
4.3 特殊字符与编码问题
特殊字符造成的问题通常都是隐性的。我看到过一个案例:两个文档标题在界面上显示完全相同,却在基于内容哈希做去重时被认为是不同文档。排查到最后,发现其中一个标题里有一个零宽空格,肉眼不可见,哈希计算时却被计入。
处理思路是建立字符清理规则。编辑器的粘贴和保存入口需要过滤掉一批不可见控制字符,只保留正常的空白符和换行符。Emoji的处理也需要单独考虑:有些Emoji由多个码点组合而成,编辑器按码点逐个解析时会把组合Emoji截断成残缺显示,甚至导致后续字符的渲染错乱。
测试代码块里我会专门放一行混排的Unicode字符:普通拉丁字母、中文全角标点、组合变音符、零宽字符、emoji、数学符号混在一起。保存再导入后逐项对照显示效果,基本能筛出一批编码层面的兼容问题。
4.4 移动端差异
移动端的富文本编辑器测试经常被忽略,原因很直接:开发环境和测试环境都以桌面为主,移动端的真机调试成本又高。但移动端的富文本交互与桌面差异巨大,触屏下的光标定位策略、虚拟键盘的弹出时机、工具栏的折叠行为,都可能影响内容编辑的结果。
一个常见的移动端问题是:在桌面编辑器里正常的多级列表嵌套操作,在移动端会失效。原因通常是移动端没有悬停状态,长按选择的交互判定逻辑与桌面不一致。另一个问题是,部分移动端浏览器在输入中文时会把拼音组合期间的临时状态带入编辑器内容里,导致内容数据混入拼音字母。
建议在测试代码块跑完桌面验证后,在移动端真机上再执行一遍完整流程。重点观察光标定位、文本选择和长按菜单这三个区域,每个都配合一下粘贴操作验证行为。以前我在移动端忽略了这个环节,上线后收到用户报告说编辑内容保存后多处格式错乱,结果重测才发现是Safari移动端对内容可编辑区域的选区处理与桌面端不同。
4.5 性能与大数据量内容
压力区的超长内容在测试时不光是为了看编辑器是否崩溃,更要关注在不同内容量级下的性能变化曲线。编辑器核心的复杂度往往集中在输入响应上:用户每敲一个字符,编辑器要重新计算选区位置、更新工具栏状态、同步文档模型。内容量越大,每一步的耗时就越长。
实测中,两万字的纯文本与一万字但包含大量嵌套块的内容,对编辑器的压力完全不同。后者因为DOM结构层级深,浏览器在布局与重绘上的开销会成倍增长。一个比较明显的信号是:当输入出现明显延迟时,打开浏览器开发者工具的“Performance”面板,会看到大量的Layout和Recalc Style耗时——这就是需要对编辑器的内部结构做优化或限制单节点子元素数量的信号。
测试代码块里建议准备两个版本:一份一万字左右的常规内容,用于日常功能验证;一份五万字以上的重型内容,用于专项性能测试。后者不需要每天跑,但每次编辑器内核升级后必须跑一遍。
4.6 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 | 解决建议 |
|---|---|---|---|
| 列表保存后序号从头开始 | 序列化未处理start属性 | 检查导出数据中的列表属性 | 在序列化逻辑中显式保留列表起始序号 |
| 粘贴文字后样式全部丢失 | 剪贴板清洗逻辑过度过滤 | 检查粘贴事件中的HTML处理函数 | 调整白名单,保留安全样式标签 |
| 内容中有不可见字符导致哈希不一致 | 粘贴时未过滤零宽字符 | 转储原始内容并检查字符码点 | 在保存入口统一执行控制字符清理 |
| 从Word复制表格只保留文本 | 表格节点被清洗规则剥除 | 检查粘贴清洗白名单是否包含表格标签 | 补充表格标签转换处理逻辑 |
| 编辑器在移动端输入中文时混入拼音字母 | 输入法组合状态被作为内容提交 | 在移动端真机复现输入过程 | 监听合成事件并拦截组合期间的DOM变化 |
| 嵌套引用块内列表层级错乱 | 嵌套块序列化只处理了顶层元素 | 导出数据观察嵌套结构是否递归处理 | 修正序列化函数递归遍历所有嵌套层级 |
5. 测试代码块在选型与回归中的妙用
测试代码块不只用于自研编辑器,在第三方编辑器选型时同样非常好用。我参与过一次编辑器选型,候选方案有三四个,每个项目的官网示例都做得光鲜亮丽,基础操作也流畅无比。但把测试代码块分别导入后,差异立刻显现:有的直接丢弃了表格内的所有块级元素,有的保存再导入后丢失链接协议,还有的在嵌套引用里出现了样式叠buff问题。官网示例看渲染效果,比不出这些细节,只有权威的复杂样本才能把候选方案的真实能力暴露出来。
选型时建议用同一份测试代码块在多个候选方案间横向对比,每个方案都记录一份验证结果表。对比维度聚焦在三个核心指标:内容无损率、粘贴清洗能力和XSS过滤完备性。这三个维度直接与在线文档系统的数据安全与用户信任度挂钩,比工具栏按钮是否好看重要得多。
同时,测试代码块应该纳入日常回归流程。我个人的做法是把它固化成一个自动化脚本,每次编辑器内核升级或依赖更新后,自动导入测试代码块并对比输出结果。已有的行为差异,脚本会高亮标出,人工确认是否为预期变更。这样既能拦截无意的功能回退,也能在多次运行后逐渐沉淀为项目的质量基线。
注意:不要试图把测试代码块做得“完美”——内容一旦固定,它作为回归基线的作用就减弱了。每发现一类新的边界问题,就在测试代码块中新增一个对应样本,再重新跑一遍全量验证,形成一个持续演进的循环。
我自己在实际项目中的体会是,测试代码块的搭建不需要一次到位。从一份简单的样例开始,逐步把踩过的坑转化成新的测试段落,比照搬一份“完整版”更有效。每增加一种特殊结构,下一次排查就多一个可复现的依据,日积月累,这套内容本身就是团队对富文本编辑器的认知沉淀。如果你还没给编辑器准备一份像样的测试代码块,不妨从今天这篇文章里的表格抄一份起点,然后用自己的问题去丰富它。这可能是投入产出比最高的测试投入之一。