- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
CKEditor 4 的 bidi 插件用于处理阿拉伯语、希伯来语等从右到左(RTL)书写的文本方向。在 4.5.8 版本中,官方修复了 RTL 方向下编辑区缺少滚动条的问题(ticket 10448),而本仓库tests/tickets/10448/目录完整保留了该缺陷的手工测试脚本、自动化回归测试与复现夹具。本文以该 ticket 的测试文档为骨架,结合 bidi 插件源码与 Bender 测试框架配置,说明该缺陷的复现方式、修复验收标准以及背后的实现原理,读者可据此在自己的 CKEditor 4 集成中排查 RTL 排版相关的溢出与滚动问题。
一、问题背景:ticket 10448 是什么
在 CHANGES.md 的 CKEditor 4.5.8 版本更新日志中,可以找到该缺陷的官方记录:
Fixed: Lack of scrollbar in the right-to-left text direction.
即:在 RTL 文本方向下,当编辑区内出现超长不可断行内容(例如一串没有空格的长单词)时,编辑区无法正常出现水平滚动条,内容被裁切或溢出,用户无法看到/滚动到被裁掉的部分。
该 ticket 被标记为bug,修复版本为4.5.8,对应的插件是 bidi(Bidirectional Text 插件,即双向文本方向控制)。当前仓库根目录 package.json 显示的开发版本为4.25.1,该修复早已合入主线并持续被回归测试守护。
二、手工测试文档逐行解读
tests/tickets/10448/2.md 是 Bender 框架下的手工测试(manual test)脚本,全文内容如下:
@bender-tags: 4.5.8, bug, trac10448 @bender-ui: collapsed @bender-ckeditor-plugins: wysiwygarea, toolbar, bidi ---- 1. Look at the editable. **Expected:** The text inside editable area should be broken between several lines.其结构分为三部分:
1. Bender 元数据头
@bender-tags: 4.5.8, bug, trac10448:声明该测试对应版本 4.5.8、类型为 bug 回归、跟踪编号为 trac10448(即 Trac 工单系统编号)。@bender-ui: collapsed:Bender 手工测试的 UI 呈现方式,表示编辑器以折叠/紧凑形式展示,方便人工聚焦编辑区。@bender-ckeditor-plugins: wysiwygarea, toolbar, bidi:声明运行该测试所需加载的插件白名单——wysiwygarea(iframe 经典编辑区)、toolbar(工具栏)、bidi(双向文本方向控制)。这正是复现 RTL 滚动条缺陷所必需的最小插件组合。
2. 测试步骤
1. Look at the editable.即:打开测试页面后,直接观察编辑区中的内容呈现效果。
3. 期望结果
Expected:The text inside editable area should be broken between several lines.
编辑区内的文本应当被断成多行(即浏览器自动换行),超长单词在达到编辑区右边界时折行显示,而不是横向溢出把编辑区撑破、或导致滚动条异常。
三、复现夹具与演示页面:超长 RTL 单词的构造
手工测试所加载的页面资源就存放在同一目录下。
复现 HTML 夹具
tests/tickets/10448/1.html 是整个 ticket 的核心复现素材,内容仅有一行:
<p dir="rtl">VerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLines</p>关键点在于:
dir="rtl":段落显式声明 RTL 文本方向;- 文本内容是一个由多个
VerylongWordThatShouldBreakIntoSeveralLines拼接而成的超长无空格连续单词。普通文本遇到空格才会断行,而无空格的长单词需要依赖浏览器的overflow-wrap/word-break行为; - 该夹具在自动化测试中通过
CKEDITOR.document.findOne( 'p' ).getOuterHtml()被直接抓取并注入编辑器(见下文第四节)。
演示页面
tests/tickets/10448/2.html 提供了人工可交互的演示页:
<div id="editor1"> <p dir="rtl">VerylongWordThatShouldBreakIntoSeveralLines…</p> </div> <script> CKEDITOR.replace( 'editor1', { width: 600 } ); </script>页面将同一段超长 RTL 文本放入editor1,并通过 CKEDITOR.replace() 以width: 600实例化编辑器。人工测试者在2.md步骤引导下打开该页,即可观察文本是否被正确断行。如果修复前,文本会横向溢出编辑区边界且不出现滚动条;修复后,文本应自动折行,编辑区宽度不再被撑破。
四、自动化回归测试:断言“段落不超出编辑区宽度”
除手工测试外,同目录的 tests/tickets/10448/1.js 给出了自动化回归测试,用精确的 DOM 度量来守护修复不被回退:
/* bender-tags: 10448 */ /* bender-ckeditor-plugins: toolbar, bidi, wysiwygarea */ ( function() { 'use strict'; bender.editor = { config: { width: 300 } }; bender.test( { _should: { ignore: { 'test paragraph fits into editable width': CKEDITOR.env.safari && CKEDITOR.env.iOS } }, 'test paragraph fits into editable width': function() { var html = CKEDITOR.document.findOne( 'p' ).getOuterHtml(), editor = this.editor, editable = editor.editable(), editableSize = editable.getSize( 'width' ), paragraphSize; this.editorBot.setData( html, function() { paragraphSize = editable.findOne( 'p' ).$.scrollWidth; assert.isTrue( editableSize >= paragraphSize ); } ); } } ); } )();测试逻辑拆解
环境声明:
bender.editor.config.width: 300将测试编辑器宽度设为 300px(比手工测试的 600px 更窄,更能暴露溢出问题);同时通过_should.ignore在Safari + iOS组合下跳过该用例——这类移动端浏览器的scrollWidth度量行为与桌面端不一致,属于已知平台差异。数据注入:从测试页 DOM 中抓取
1.html里的<p dir="rtl">超长文本,经editorBot.setData()写入编辑器。度量比较:
editableSize = editable.getSize( 'width' ):编辑区(iframe body)的布局宽度;paragraphSize = editable.findOne( 'p' ).$.scrollWidth:段落 DOM 原生scrollWidth,即内容滚动宽度;assert.isTrue( editableSize >= paragraphSize ):断言编辑区宽度不小于段落内容宽度。
如果段落内容横向溢出(修复前状态),
scrollWidth会大于编辑区宽度,断言失败;修复后浏览器正确折行,scrollWidth被限制在编辑区宽度内,断言通过。这一度量方式与“断成多行”的人工期望完全对应——折行成功意味着内容宽度不再超出容器。
五、底层原理:bidi 插件如何管理 RTL 方向
要理解为什么dir="rtl"的长文本会触发该类缺陷,需要看 bidi 插件如何把方向写入内容。
1. 方向写入的核心命令
plugins/bidi/plugin.js 定义了bidiCommand( dir )工厂,注册了bidirtl(设为 RTL)与bidiltr(设为 LTR)两个命令:
function bidiCommand( dir ) { return { context: 'p', allowedContent: { 'h1 h2 h3 h4 h5 h6 table ul ol blockquote div tr p div li td': { propertiesOnly: true, attributes: 'dir' } }, requiredContent: 'p[dir]', refresh: function( editor, path ) { ... }, exec: function( editor ) { ... } }; }命令执行时通过switchDir()完成方向切换,其核心逻辑(plugin.js):
function switchDir( element, dir, editor, database ) { ... element.removeStyle( 'direction' ); if ( useComputedState ) { element.removeAttribute( 'dir' ); if ( dir != element.getComputedStyle( 'direction' ) ) { element.setAttribute( 'dir', dir ); } } else { element.setAttribute( 'dir', dir ); } editor.forceNextSelectionCheck(); }可见插件优先清理元素上的direction样式,再按useComputedState配置决定是写入dir属性还是依赖计算样式。dir属性最终由浏览器渲染引擎决定文本方向,因此在测试夹具中直接使用<p dir="rtl">等价于用户点击工具栏“从右到左”按钮后的结果。
2. 方向的读取与回显
core/dom/element.js 的getDirection()完整实现了方向解析的回退链:
getDirection: function( useComputed ) { if ( useComputed ) { return this.getComputedStyle( 'direction' ) || this.getDirection() || this.getParent() && this.getParent().getDirection( 1 ) || this.getDocument().$.dir || 'ltr'; } else { return this.getStyle( 'direction' ) || this.getAttribute( 'dir' ); } }非计算模式下先查 CSSdirection样式、再查dir属性;计算模式下依次向上回溯父元素与文档级dir,最终兜底'ltr'。bidi 插件工具栏按钮的状态回显(plugin.js)正是基于该 API 判断当前选区方向的。
3. 方向变更的事件通知
插件还通过dirChangeNotifier(plugin.js)重写了setStyle/removeStyle/setAttribute/removeAttribute,在方向属性变化时触发dirChanged与contentDirChanged事件,并给工具栏添加cke_mixed_dir_content类提示混合方向内容。这也说明 RTL 支持是贯穿“写入 → 渲染 → UI 状态”的全链路能力。
4. 可用的配置项
config.useComputedState(布尔):决定方向读写基于计算样式还是显式dir属性,默认值为false,即显式写入dir属性——这也是测试夹具中dir="rtl"能被插件体系识别的原因;config.enterMode(CKEDITOR.ENTER_P/CKEDITOR.ENTER_BR/CKEDITOR.ENTER_DIV):影响命令执行时对段落块的处理方式,见bidiCommand的exec中对getFullySelected与段落迭代的调用。
六、如何本地运行该 ticket 的测试
本仓库使用 Bender 测试框架(见 bender.js 配置与 package.json 中的benderjs系列 devDependencies)。要验证 ticket 10448 的修复状态,可按以下方式操作:
- 安装依赖并初始化 Bender:
npm install npx bender init - 运行手工测试:启动 Bender 服务器后,在测试树中定位
tests/tickets/10448下的2.html,按 2.md 的步骤观察编辑区文本是否断行;1.js作为自动化用例会直接参与bender run的回归批次。 - 自动化测试的关键断言路径:测试将编辑区宽度设为 300px(1.js),注入超长 RTL 文本后断言
editableSize >= paragraphSize,任何导致横向溢出的回归都会让该用例失败。
七、总结与排查建议
ticket 10448 是 CKEditor 4 在 RTL 场景下的一个典型布局缺陷:超长不可断行文本在dir="rtl"段落中横向溢出且不出现滚动条。仓库通过 tests/tickets/10448/2.md(人工验收)、1.html(复现夹具)、2.html(演示页)与 1.js(自动化断言)四件套,将修复固化为可持续回归的测试资产。
若你在自己的 CKEditor 4 集成中遇到类似 RTL 溢出问题,可参照以下思路排查:
- 确认内容中
dir="rtl"段落内的超长无空格串是否被 CSS 正确折行,重点检查编辑区容器的overflow与scrollWidth表现; - 核对
useComputedState配置对dir属性写入方式的影响(plugins/bidi/plugin.js); - 复用 1.js 中的
editableSize >= scrollWidth断言思路,为你的定制编辑区补充宽度溢出回归用例。
- 前端
- UI组件
【免费下载链接】ckeditor4
The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.
相关推荐
进度条RTL方向行为:CKEditor 4 imagebase 手动测试用例与源码实现全解析
进度条RTL方向行为:CKEditor 4 imagebase 手动测试用例与源码实现全解析 CKEditor 4 自 4.9.0 起在 imagebase 插
前端UI组件CKEditor 4 粘贴对话框长单词换行修复实战:从手动测试用例到 `word-break` 样式实现
CKEditor 4 粘贴对话框长单词换行修复实战:从手动测试用例到 word break 样式实现 导读 本文围绕 CKEditor 4 中一个真实且典型的前
前端UI组件CKEditor 4 Copy Formatting 插件从右到左选区失效问题:Bender 手动测试 `selectionfromrighttoleft` 全解析
CKEditor 4 Copy Formatting 插件从右到左选区失效问题:Bender 手动测试 selectionfromrighttoleft 全解析
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考