如何系统测试富文本编辑器?Draftail 的 Jest、Storyshots 与 Puppeteer 集成测试全景
【免费下载链接】draftail📝🍸 A configurable rich text editor built with Draft.js项目地址: https://gitcode.com/gh_mirrors/dr/draftail
富文本编辑器(Rich Text Editor)是最难测试的组件之一:键盘快捷键、工具栏交互、粘贴、截图回归、内存占用……任何一个环节都可能悄悄坏掉。Draftail 是基于 Draft.js 构建的可配置富文本编辑器组件,它用Jest 单元测试 + Storyshots 冒烟测试 + Puppeteer 浏览器集成测试 + 性能基准这套四层组合拳,把编辑器测试覆盖得相当完整。本文带你拆解这套富文本编辑器测试方案,新手也能照搬到自己的项目中。
一、测试体系全景:四层防线各司其职
🛡️ 先看全局。Draftail 把测试分成四个层次,从快到慢、从细到粗:
| 层级 | 工具 | 测试什么 | 代表文件 |
|---|---|---|---|
| 单元测试 | Jest + Enzyme + Testing Library | 单个组件、行为逻辑 | src/api/behavior.test.ts |
| 冒烟测试 | Storyshots | 所有 Story 能否正常渲染 | tests/smoke/storyshots.test.js |
| 集成测试 | Puppeteer + Storybook | 真实浏览器中的完整交互 | tests/integration/examples.test.js |
| 性能基准 | react-benchmark | 内存占用与编辑速度 | tests/performance/markov_draftjs_41.js |
对应的 npm 脚本在 package.json 中定义:npm test(单测)、npm run test:coverage(覆盖率)、npm run test:integration(集成测试)、npm run test:performance(性能)。
二、第一层:Jest 单元测试——在 jsdom 中跑组件
单元测试的配置写在根目录的jest.config.js中,几个关键设置值得学习:
- ts-jest 转换:直接运行 TypeScript / React 源码,无需预编译;
- 样式打桩:所有
.scss导入被映射到tests/styleMock.js,测试不关心样式细节; - jsdom 环境:模拟浏览器 DOM,让 React 组件可以挂载;
- Enzyme + enzyme-to-json:配合快照序列化,把组件渲染成易读文本。
全局初始化逻辑在tests/setupTest.js中完成:配置 Enzyme 适配器、引入@testing-library/jest-dom断言库。例如tests/rtl.test.js就用 Testing Library 验证了编辑器的 ARIA 属性可访问性——按role="textbox"就能选中编辑器,这是无障碍测试的正确姿势。
单元测试贴近源码编写,比如src/api/DraftUtils.test.ts测试内容状态转换、src/components/DraftailEditor.test.tsx测试主组件渲染。
三、第二层:Storyshots 冒烟测试——所有示例一次过
Storyshots 的思路非常巧妙:Draftail 用 Storybook 组织了大量编辑器示例(简单编辑器、自定义工具栏、各插件场景),tests/smoke/storyshots.test.js会自动遍历每一个 Story 并用 Enzyme mount 挂载:
initStoryshots({ suite: "Storyshots smoke tests", test: ({ story }) => { const storyElement = story.render(); mount(storyElement); }, });只要有任何一个示例渲染时抛错,CI 立刻失败。对于"示例即文档"的组件库来说,这是成本最低的一道回归防线。
四、第三层:Puppeteer 集成测试——真浏览器里的真操作
这是全文最值得借鉴的部分。编辑器很多行为(快捷键、选区、输入自动转换)在 jsdom 里根本模拟不出来,Draftail 直接开一个真实 Chrome。
1. 自定义 Jest 环境,全局只启一个浏览器
集成测试使用独立的 Jest 配置tests/integration/jest.config.js,包含三个角色:
tests/integration/setup.js(全局启动):用puppeteer.launch启动无头 Chrome,把 WebSocket 端点写入临时文件;如果不是 watch 模式,还会用 Express 在 5001 端口起一个静态服务,托管构建好的 Storybook 产物;tests/integration/teardown.js(全局收尾):关闭浏览器、清理临时文件、关掉服务;tests/integration/PuppeteerEnvironment.js(测试环境):继承 Node 的 TestEnvironment,从临时文件读取端点并通过puppeteer.connect复用同一个浏览器实例,避免每个测试文件都冷启动 Chrome。
watch 模式下则改为连接本地 Storybook 开发服务器(9001 端口),改代码不用重新构建就能调试测试——这套"双模式"设计很实用。
2. 模拟真人:打字、点工具栏、按快捷键
tests/integration/examples.test.js是"功能验收"的范本。它打开 Storybook 测试页,然后像真人一样操作:
- 选中 "Bold" 后点击工具栏加粗按钮,校验内容状态与快照一致;
- 输入
### H3、* UL、---验证 Markdown 自动转换; Shift + Enter验证软换行;- 点击 IMAGE / LINK 按钮并在弹窗中填写,验证实体插入。
每个用例结束后,从sessionStorage读出编辑器的 ContentState,删除不稳定的 block key 再与快照比对——用数据快照替代 UI 断言,既稳定又精确。
此外还接入了jest-axe做无障碍(a11y)自动审计,任何 ARIA 违规都会让测试失败,富文本编辑器能少踩很多坑。
3. 截图回归:像素级防走样
tests/integration/regression.test.js用jest-image-snapshot对编辑器渲染结果截图比对。两个细节很关键:
- 先注入
normalize-rendering.css,抹掉不同操作系统间的字体渲染差异; - 设置
failureThreshold: 0.003(0.3% 容差),容忍抗锯齿带来的微小噪点。
基准截图存放在tests/integration/__image_snapshots__/目录,UI 一旦意外变化,CI 就能立刻发现。
4. 性能护栏:内存与速度都有上限
tests/integration/performance.test.js在真实页面里用window.performance.memory读取 JS 堆内存:简单编辑器输入几行文字后堆内存不得超过 75MB(25MB × 3 倍缓冲),加载 Markov 大文档场景不得超过 57MB。同时还会读取基准页面输出的平均编辑耗时,要求不超过约 141ms。给性能设"预算"并纳入 CI,是防止编辑器越改越卡的长效机制。
五、一键跑完所有测试:test:ci
整套流程由一条命令串起来(见 package.json 的test:ci脚本):
npm run test:ci它依次执行:代码检查(ESLint + Stylelint + Prettier)→ 覆盖率单测 → 构建库与 Storybook → Puppeteer 集成测试 → 性能基准。想自己跑一遍,只需克隆仓库:
git clone https://gitcode.com/gh_mirrors/dr/draftail cd draftail npm install npm run test:ci六、新手可以直接抄走的 4 个测试思路
📌 从 Draftail 的实践里,可以提炼出测试富文本编辑器(乃至任何复杂前端组件)的通用方法论:
- 分层设防:单测保证逻辑,冒烟保证"都能渲染",集成测试保证"真的能用",性能测试保证"不会越用越卡"——四层缺一,防线就有漏洞;
- 测试对象就是 Storybook 示例:把文档示例变成可测试资产,一举两得;
- 断言数据而非像素:优先比对编辑器内部 ContentState 快照,截图只作 UI 兜底,并用容差吸收渲染噪声;
- 把 a11y 和性能预算写进 CI:用 jest-axe 和内存/耗时阈值,让质量指标可量化、可回归。
按这套思路搭建,你的富文本编辑器也能拥有"敢发版"的底气。🚀
【免费下载链接】draftail📝🍸 A configurable rich text editor built with Draft.js项目地址: https://gitcode.com/gh_mirrors/dr/draftail
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考