BlockNote 端到端测试专用示例编辑器:testing 示例的设计与 e2e 测试基础设施解析
前端富文本UI组件AI 应用【免费下载链接】BlockNoteA React Rich Text Editor thats block-based (Notion style) and extensible. Built on top of Prosemirror and Tiptap.项目地址https://gitcode.com/gh_mirrors/bl/BlockNote点击查看免费下载导读本文围绕 BlockNote 仓库中一个特殊的基础示例examples/01-basic/testing展开它不是一个面向用户的演示应用而是专门为端到端e2e测试设计的“测试编辑器”被仓库的 Playwright 浏览器测试套件直接挂载使用。读完本文你将理解该示例的最小实现含 base64 文件上传兜底逻辑、它如何通过examples别名被 basics.test.tsx 等测试导入渲染、测试工具层选择器常量、等待/快照/截图断言如何工作以及整套 e2e 测试的运行方式与多浏览器实例配置。一、testing 示例的定位为 e2e 测试而生的最小编辑器在 BlockNote 仓库的 examples/01-basic 目录下绝大多数示例01-minimal、02-block-objects、03-multi-column等都承担着“向开发者展示某类 API 用法”的文档职责。而 testing/README.md 用两句话交代了它的唯一使命This example is meant for use in end-to-end tests.也就是说这个示例不是给人看的功能演示而是给机器跑的测试基座。它的价值体现在两方面提供一个稳定、无副作用的编辑器宿主测试需要反复挂载、卸载编辑器实例因此该示例不包含任何外部依赖无后端上传、无协作服务、无 AI 调用保证任何浏览器环境下渲染结果一致与测试基础设施解耦测试代码通过路径别名导入示例组件示例自身不感知测试逻辑职责单一。这一点可以从 tests/src/examples.d.ts 的注释中得到印证e2e 测试通过import App from examples/group/name/src/App挂载示例应用examples别名由 Vite 在运行时解析见下文第五节。二、源码拆解一个带文件上传兜底的最小编辑器src/App.tsx 是整个示例的全部业务代码仅 26 行import blocknote/core/fonts/inter.css; import { BlockNoteView } from blocknote/mantine; import blocknote/mantine/style.css; import { useCreateBlockNote } from blocknote/react; // Uploads a file by encoding it as a base64 data URL. In a real app youd // replace this with an upload to your own backend that returns a URL to the // stored file. async function uploadFile(file: File) { return new Promisestring((resolve, reject) { const reader new FileReader(); reader.onload () resolve(reader.result as string); reader.onerror () reject(reader.error); reader.readAsDataURL(file); }); } export default function App() { // Creates a new editor instance. const editor useCreateBlockNote({ uploadFile, }); // Renders the editor instance using a React component. return BlockNoteView editor{editor} /; }2.1 三处导入样式、UI 外壳与 React 桥接blocknote/core/fonts/inter.css引入编辑器默认字体Inter保证测试环境下文字渲染与生产一致blocknote/mantine的BlockNoteView与blocknote/mantine/style.css使用 Mantine 主题的编辑器 UI 外壳blocknote/react的useCreateBlockNoteReact 侧创建编辑器实例的 Hook。这与 examples/01-basic/01-minimal/src/App.tsx 等基础示例的引入方式保持一致从源码结构看testing示例正是以最小可运行形态覆盖默认编辑器能力。2.2 uploadFilebase64 兜底的文件上传uploadFile是本示例中唯一一段业务逻辑用FileReader.readAsDataURL把文件编码为 base64 Data URL 后作为“上传结果”返回。源码注释明确说明——真实应用中应替换为上传到自有后端并返回存储 URL 的实现。它在测试中的意义在于离线可用e2e 测试尤其是图片类测试无需真实网络直接以 Data URL 注入图片即可结果可断言测试可以通过检查img节点的src是否为data:开头来验证上传流程参见 tests/src/end-to-end/images/images.test.tsx 所在目录的测试套件。2.3 入口与宿主应用入口 main.tsx 用createRoot渲染App /并包裹React.StrictModeindex.html 提供#root挂载点。这些文件都标注了“AUTO-GENERATED FILE, DO NOT EDIT DIRECTLY”说明示例脚手架由仓库脚本统一生成保证所有示例结构一致。三、测试如何挂载该示例以 basics.test.tsx 为例端到端测试套件位于 tests/src/end-to-end共约数十个测试目录basics、colors、comments、copypaste、dragdrop、tables、mobile 等。其中 basics/basics.test.tsx 是直接挂载testing示例的最简案例import App from examples/01-basic/testing/src/App; import { beforeEach, describe, expect, test } from vite-plus/test; import { render } from vitest-browser-react; import { userEvent } from ../../utils/context.js; import { EDITOR_SELECTOR } from ../../utils/const.js; import { waitForSelector } from ../../utils/editor.js; beforeEach(async () { await render(App /); await waitForSelector(EDITOR_SELECTOR); }); describe(Basic typing functionality, () { test(should allow me to type content, async () { const editor await waitForSelector(EDITOR_SELECTOR); await userEvent.click( document.querySelectorAll(${EDITOR_SELECTOR} div)[3] as HTMLElement, ); await userEvent.keyboard(hello world); expect(editor.textContent).toBe(hello world); }); });这个测试完整走通了“渲染示例 → 等待编辑器就绪 → 模拟交互 → 断言结果”的 e2e 标准链路render(App /)来自vitest-browser-react直接把示例组件渲染进真实浏览器 DOM测试运行在浏览器中而非 jsdomwaitForSelector(EDITOR_SELECTOR)轮询等待.bn-editor节点挂载确保 ProseMirror 视图初始化完成userEvent.clickuserEvent.keyboard模拟真实点击与键盘输入hello worldexpect(editor.textContent).toBe(hello world)断言编辑器 DOM 文本与输入完全一致。从源码结构看userEvent来自 tests/src/utils/context.ts 的导出封装它基于浏览器环境下的用户事件模拟实现。四、测试工具层选择器常量与断言工具4.1 选择器常量const.tstests/src/utils/const.ts 集中定义了 e2e 测试依赖的 DOM 选择器它们与 BlockNote 的 DOM 结构约定强相关。核心常量如下常量选择器用途EDITOR_SELECTOR.bn-editor编辑器根节点绝大多数测试的入口BLOCK_CONTAINER_SELECTOR[data-node-typeblockContainer]块容器BLOCK_GROUP_SELECTOR[data-node-typeblockGroup]块组PARAGRAPH_SELECTOR[data-content-typeparagraph]段落块H_ONE_BLOCK_SELECTOR等[data-content-typeheading]...各级标题块IMAGE_SELECTOR/PDF_SELECTOR/TABLE_SELECTOR[data-content-typeimage/pdf/table]多媒体/表格块DRAG_HANDLE_SELECTOR[data-testdragHandle]拖拽手柄SLASH_MENU_SELECTOR.bn-suggestion-menu斜杠菜单suggestion 菜单被传送到编辑器容器内故按类名匹配ITALIC_BUTTON_SELECTOR等[data-testitalic]等格式化工具栏按钮这些选择器是测试与编辑器 DOM 契约的“公共接口”任何 DOM 结构调整都会通过这些常量在测试中暴露。testing示例使用默认主题与默认块架构因此这些选择器对它完全适用。4.2 编辑器断言工具editor.tstests/src/utils/editor.ts 提供了可复用的等待与断言函数waitForSelector(selector, { timeout 5000 })基于vi.waitFor轮询元素未出现时抛错默认超时 5 秒waitForSelectorDetached等待元素从 DOM 移除用于菜单关闭、块删除等场景focusOnEditor点击编辑器使其获得焦点waitForTextInEditor(text)等待编辑器文本包含指定内容getDoc()直接读取全局window.ProseMirror的getJSON()返回 ProseMirror 文档的 JSON 表示——源码注释特别说明测试运行在浏览器内无需page.evaluate往返compareDocToSnapshot(name)将文档 JSON 与./__snapshots__/name.json快照比对实现“文档级快照测试”expectElement(...)/matchPageScreenshot(name)视觉回归断言前者可对任意元素做截图对比后者对document.body整页截图可捕获传送到 body 的菜单/工具栏。matchPageScreenshot的注释还透露了快照命名策略视觉基线由 Vitest 按“浏览器 平台”自动命名而文档 JSON 快照与浏览器无关因此跨 chromium/firefox/webkit 三个实例共享同一份快照。五、e2e 运行基础设施vite.config.browser.ts 关键设计整个 e2e 套件的运行配置集中在 tests/vite.config.browser.ts其中与testing示例直接相关的机制包括5.1 examples 别名与源码级解析alias: { ...blockNoteSrcAliases, shared: path.resolve(__dirname, ../shared), examples: path.resolve(__dirname, ../examples), },examples指向仓库的examples目录因此测试代码里import App from examples/01-basic/testing/src/App会解析到 testing/src/App.tsxblockNoteSrcAliases把每个blocknote/*包core、react、mantine、shadcn 等解析到各自的src/目录即测试运行时直接从源码转译包代码不依赖预先构建的 dist——这使修改包源码无需重建 Docker 镜像即可生效TypeScript 侧的配套声明在 tests/src/examples.d.ts以环境模块declare module examples/*声明默认导出为 React 组件避免 tsc 下沉到示例源码破坏 composite 构建TS6059。5.2 多浏览器实例矩阵配置中注册了五个 Playwright 实例这是理解“测试编辑器为何要在多种环境下保持一致”的关键实例浏览器视口说明chromiumChromium1280×720桌面主实例附加--no-sandbox等容器运行参数firefoxFirefox1280×720桌面兼容性webkitWebKit1280×720桌面兼容性androidChromium移动 UA393×727模拟 Android 12 / Chrome 151isMobile: true, hasTouch: true让 prosemirror-view 走 Android 输入路径iosWebKitiPhone UA393×727模拟 iPhone / iOS 18 Safari覆盖 iOS 输入路径如原生 split 回读桌面实例通过DESKTOP_EXCLUDE排除end-to-end/mobile/**移动实例则用include限定各自专属套件。此外还做了全局兜底配置截图断言全局允许 2% 像素差异allowedMismatchedPixelRatio: 0.02以吸收多浏览器反锯齿/字体渲染差异testTimeout: 30000、retry: 2缓解三浏览器共容器运行的偶发资源争抢fileParallelism: false单浏览器单 worker避免 CPU 饱和导致超时。5.3 测试前置与 iframe 尺寸tests/vitestSetup.browser.ts 在每轮测试前完成两件重要工作将测试 iframe 尺寸设置为与浏览器窗口一致的 1280×720移动实例为 393×727避免 Vitest 默认 iframe 过窄导致菜单换行、截图失真注入样式.bn-container { max-width: 731px; margin: 0 auto; padding-top: 8px; }与 BlockNote 官网示例页的编辑框宽度对齐使截图基线与线上展示一致。六、运行方式脚本与 Docker 工作流6.1 脚本入口tests/package.json 提供了两条 e2e 命令{ scripts: { test:e2e: vp test -c vite.config.browser.ts --run, test:e2e:updateSnaps: vp test -c vite.config.browser.ts --run -u } }test:e2e以--run单次执行全部浏览器测试test:e2e:updateSnaps追加-u更新所有文档 JSON 快照与视觉基线新增/修改基线时使用。6.2 Docker 运行脚本docker-run.shtests/docker-run.sh 是官方推荐的本地执行方式。它的核心设计是“镜像只装依赖、源码运行时挂载”镜像blocknote-e2e安装依赖但不构建任何包见 tests/Dockerfile 配套说明启动时把每个packages/*/src以-v方式绑定挂载进容器与 5.1 节的源码级解析配合编辑包源码后无需重建镜像即可复测脚本会用内容哈希标签自动判断依赖/示例是否变化必要时自动重建镜像典型用法为tests/docker-run.sh [docker 参数] -- [vp 参数]例如tests/docker-run.sh -- -t Basic typing可只跑指定测试。6.3 浏览器测试与单元测试的边界需要说明的是e2e 套件不仅包含tests/src/end-to-end/**/*.test.tsx还会运行各包源码内联的*.browser.test.{ts,tsx}如 canvas 栅格化、Mermaid 渲染等浏览器专属单测见include配置include: [ ./src/end-to-end/**/*.test.tsx, ../packages/*/src/**/*.browser.test.{ts,tsx}, ],这与testing示例无直接关系但解释了为何该配置的include范围覆盖了包目录。七、小结从测试编辑器到测试契约examples/01-basic/testing虽然只有一行业务组件却在 BlockNote 的质量保障体系中扮演枢纽角色对测试代码它是稳定、可复现的渲染宿主src/App.tsx对编辑器 DOM它通过 const.ts 中的选择器与测试建立“DOM 契约”任何样式类或data-*属性的变更都会被测试直接捕获对 CI它与 vite.config.browser.ts 的源码级别名、五实例浏览器矩阵、快照断言共同构成可离线、可并发的回归防线。若你希望在自有项目中复刻这套模式最直接的做法是仿照本示例维护一个“最小测试编辑器”应用导出其 App 组件供测试挂载将编辑器根节点、块类型、工具栏按钮的选择器集中到常量文件中再用浏览器模式测试框架本仓库使用 vite-plus vitest-browser-react Playwright驱动真实输入与截图断言。当编辑器 DOM 契约稳定后这套测试基座即可长期复用于功能回归与视觉回归。相关文件索引示例本体README.md、src/App.tsx、main.tsx直接挂载该示例的测试basics.test.tsx测试工具层const.ts、editor.ts、examples.d.ts运行配置vite.config.browser.ts、vitestSetup.browser.ts、tests/package.json、docker-run.sh赞分享前端富文本UI组件AI 应用【免费下载链接】BlockNoteA React Rich Text Editor thats block-based (Notion style) and extensible. Built on top of Prosemirror and Tiptap.项目地址https://gitcode.com/gh_mirrors/bl/BlockNote点击查看免费下载相关推荐Multilingual-E5-large-instruct与其他嵌入模型对比为什么选择它Multilingual E5 large instruct与其他嵌入模型对比为什么选择它 Multilingual E5 large instruct是一前端富文本UI组件AI 应用Base UI 端到端测试指南Playwright Vite 的 e2e 基础设施解析Base UI 端到端测试指南Playwright Vite 的 e2e 基础设施解析 端到端e2e测试是验证 Base UI 组件在真实浏览器环境下前端UI组件Kubernetes E2E 测试框架 test/e2e/framework 源码级解析面向 Ginkgo 的端到端测试基础设施Kubernetes E2E 测试框架 test/e2e/framework 源码级解析面向 Ginkgo 的端到端测试基础设施 Kubernetes 仓库中云原生容器编排集群管理微服务上一篇Bitwarden server 如何用 k6 对登录端点做固定 QPS 在线压测并检查延迟阈值下一篇TCP Option Address (TOA)深度解析如何从TCP头部选项高效提取源IPv4地址创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

React Static 配置完全指南:深入 `static.config.js` 的每一项参数

React Static 配置完全指南:深入 `static.config.js` 的每一项参数

React Static 配置完全指南:深入 static.config.js 的每一项参数 【免费下载链接】react-static ⚛️ 🚀 A progressive static site generator for React. 项目地址: https://gitcode.com/gh_mirrors/re/react-static static.config.js 是 React…

2026/9/24 16:29:34 阅读更多 →
G6 网格线插件(GridLine)完全指南:画布对齐辅助线的配置、跟随模式与实现原理

G6 网格线插件(GridLine)完全指南:画布对齐辅助线的配置、跟随模式与实现原理

数据可视化前端图表库 【免费下载链接】G6 ♾ A Graph Visualization Framework in JavaScript. 项目地址: https://gitcode.com/gh_mirrors/g6/G6 点击查看 免费下载 GridLine 是 G6 内置的画布辅助线插件,通过绘制可定制的网格背景,帮助用…

2026/9/24 16:29:34 阅读更多 →
PiKVM 自定义命令按钮实战:KVMD 3.36 的 pseudo-GPIO CMD 驱动与 Web 菜单集成指南

PiKVM 自定义命令按钮实战:KVMD 3.36 的 pseudo-GPIO CMD 驱动与 Web 菜单集成指南

文档教程 【免费下载链接】pikvm Open and inexpensive DIY IP-KVM based on Raspberry Pi 项目地址: https://gitcode.com/gh_mirrors/pi/pikvm 点击查看 免费下载 KVMD 3.36 为 PiKVM 的 Web 界面菜单引入了期待已久的"自定义命令按钮"功能&#xff1a…

2026/9/24 16:29:34 阅读更多 →

最新新闻

从零构建你的AI Agent发行版:Profile、技能与生产部署全指南

从零构建你的AI Agent发行版:Profile、技能与生产部署全指南

我以前装 Linux 有个习惯:拿到一个发行版镜像,第一件事不是急着安装,而是先翻它的默认配置。包管理器是什么,桌面环境是哪套,预装工具链齐不齐,默认 shell 是 bash 还是 zsh。Ubuntu 用 apt,Arc…

2026/9/24 23:37:27 阅读更多 →
军工OA系统中CKEditor配置PDF转存方案与踩坑实践

军工OA系统中CKEditor配置PDF转存方案与踩坑实践

军工行业OA系统如何配置CKEditor的PDF转存功能?先说明白一个场景:你在一家军工单位的OA系统里,领导要求写份报告,编辑器用的是CKEditor,正文填完了,得输出一份固定版式的PDF,带编号、带水印、带…

2026/9/24 23:37:27 阅读更多 →
CKEditor集成PDF转图片与文本:军工OA内网部署实战解析

CKEditor集成PDF转图片与文本:军工OA内网部署实战解析

去年我配合一个军工单位的OA系统做二次开发,需求方提了一个很具体的要求:在CKEditor富文本编辑器里,用户上传PDF文件后,系统要能自动把PDF内容转存成图片和文本,方便编辑正文时直接预览,而不是让每个人下载…

2026/9/24 23:37:27 阅读更多 →
电子病历EMR结构化编辑器源码解析:从数据模型到二次开发实战

电子病历EMR结构化编辑器源码解析:从数据模型到二次开发实战

站在医疗信息化的角度看,EMR(电子病历)从来都不是一个“能打字的Word”那么简单。尤其当你翻开一套智慧电子病历源码,第一眼看到“免费结构化编辑器”这几个字,就该意识到:这玩意儿真正值钱的地方&#xff…

2026/9/24 23:37:27 阅读更多 →
400KHz下USB转I2C总线速率测试与Excel扫描方案

400KHz下USB转I2C总线速率测试与Excel扫描方案

1. 项目背景与测试目标拆解1.1 为什么要在400KHz下测I2C总线速率I2C总线的标准模式是100KHz,快速模式是400KHz,高速模式能到3.4MHz。但实际项目里,400KHz这个档位是最微妙的——它刚好卡在“大部分MCU都能跑”和“信号完整性开始找麻烦”的临…

2026/9/24 23:37:27 阅读更多 →
Java IO流与面向对象:从管道思想到文件读写实战

Java IO流与面向对象:从管道思想到文件读写实战

不少Java新手学完面向对象三大特性之后,兴致勃勃地冲进IO流,结果被一堆Input、Output、Stream、Reader、Writer的类名砸得晕头转向。明明每个类单独看都能理解,合在一起就不知道谁该搭配谁,更不知道项目里到底该用哪个。作为一个被…

2026/9/24 23:36:27 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →