news 2026/10/17 6:38:55

CKEditor 4 Rich Combo 悬停与按下状态的回归测试:解读 desktoprichcombo 手动测试与样式状态机实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CKEditor 4 Rich Combo 悬停与按下状态的回归测试:解读 desktoprichcombo 手动测试与样式状态机实现
  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载

本篇技术指南围绕 CKEditor 4 仓库中的手动测试文档 tests/plugins/toolbar/manual/hover/desktoprichcombo.md 展开,讲解桌面端工具栏 Rich Combo(下拉组合框,如样式、格式选择器)的悬停(hover)与按下(toggled on)视觉状态的验收标准。读者将掌握:如何读懂并复跑该 Bender.js 手动测试、Rich Combo 三态状态机(cke_combo_on/cke_combo_off/ disabled)在源码与皮肤 CSS 中的具体实现,以及该测试背后的 Bug #4611(移动端误触悬停样式)修复动机。

一、测试文档定位:Bug #4611 与手动回归测试

desktoprichcombo.md是 CKEditor 4 仓库中一套**手动测试(manual test)**的说明文档,文件头部三行元数据即指明了它的用途:

@bender-tags: 4.16.1, 4611, bug @bender-ui: collapsed @bender-ckeditor-plugins: wysiwygarea, toolbar, stylescombo, format
  • @bender-tags: 4.16.1, 4611, bug:该测试标记为4.16.1版本引入、与issue #4611相关、类型为bug 回归验证;
  • @bender-ui: collapsed:手动测试页面以折叠 UI 呈现,便于直接观察工具栏;
  • @bender-ckeditor-plugins:页面加载的插件集合为wysiwygarea、toolbar、stylescombo、format——前两者提供编辑区域与工具栏框架,后两者恰好是仓库中两个典型的 Rich Combo 使用者(样式组合框与格式组合框)。

Issue #4611 的修复记录可以在 CHANGES.md 中找到:

#4611: [Android, iOS] Fixed: Incorrect hover styles for buttons in the toolbar on mobile devices.

即:移动端设备由于不存在"悬停"概念,却错误地应用了悬停样式,导致按钮视觉状态混乱。本次桌面端测试正是为了保证修复没有破坏桌面端的正确行为——桌面端必须保留悬停样式,且点击后的按下状态必须正常。

二、测试步骤原文解读(Moono-Lisa 皮肤)

测试文档的正文部分(## Procedure (Moono-Lisa))要求测试者在Moono-Lisa皮肤下按以下三步执行:

  1. 检查 Rich Combo 是否有悬停样式(背景变深)
    • 期望结果:存在悬停样式;
    • 非期望结果:不存在悬停样式。
  2. 检查点击 Rich Combo 是否激活其按下(toggled on)状态(背景变浅)
    • 期望结果:被点击的 Combo 呈现按下状态;
    • 非期望结果:被点击的 Combo 没有按下状态。
  3. 对第二个编辑器重复以上流程。

第 3 步中的"第二个编辑器"指向测试页模板中的两个编辑实例:一个基于Iframe(wysiwygarea提供),另一个基于Div(通过divarea插件提供),详见下文第三节。

同一目录下的皮肤/设备变体矩阵

tests/plugins/toolbar/manual/hover/目录下还存在同一逻辑的多个变体,共同构成一套完整的回归矩阵:

设备皮肤文件
DesktopMoono-Lisadesktoprichcombo.md(本文档)
DesktopMoonodesktoprichcombomoono.md
DesktopKamadesktoprichcombokama.md
Android三种皮肤androidrichcombo*.md
iOS三种皮肤iosrichcombo*.md

以 desktoprichcombomoono.md 为例,Moono 皮肤的测试额外增加了一步:

  1. Check if toggled on rich combos have hover styles (additional shadow, "deeper pressed in" combo).

即在 Moono 皮肤下,已处于按下状态的 Combo 仍要具备悬停样式(附加阴影,呈现"更深按下"效果)。这说明不同皮肤对状态组合的视觉定义并不完全相同,测试文档正是用来逐皮肤锁定这些细节。

三、测试页面模板:两个编辑器实例与皮肤动态注入

所有 hover 手动测试共用同一个页面模板 tests/plugins/toolbar/manual/hover/template.html。模板的核心逻辑如下:

  1. 页面包含两个contenteditable容器:#iframe-editor(标题为 "Iframe-based editor")和#div-editor(标题为 "Div-based editor");
  2. 脚本通过当前页面 URL 中的路径名解析测试目标环境与皮肤:
var pathRegex = /(android|ios|desktop)(?:button|richcombo)(moono|kama)?/i, match = location.pathname.match( pathRegex ), expectedEnv = match[ 1 ], skin = match[ 2 ] ? match[ 2 ] : 'moono-lisa';

即:文件名为desktoprichcombo.md时解析出expectedEnv = 'desktop'、skin = 'moono-lisa';若是desktoprichcombomoono.md则skin = 'moono',以此类推。皮肤名直接作为skin配置传入编辑器:

CKEDITOR.replace( 'iframe-editor', { skin: skin } ); CKEDITOR.replace( 'div-editor', { extraPlugins: 'divarea', skin: skin } );
  1. 模板内置了isIgnoredEnv()环境判定:通过navigator.userAgent判断当前是 Android、iOS 还是桌面环境。若与文件名期望的环境不匹配,则调用bender.ignore()跳过该测试。例如在 Android 真机上打开desktoprichcombo测试时会被自动忽略,从而保证每个测试只在正确的设备类别上执行。

四、源码级原理(一):Rich Combo 的 DOM 结构

Rich Combo 的渲染模板定义在 plugins/richcombo/plugin.js 中。其外层容器为cke_combo,可点击区域为a.cke_combo_button,内部依次是文本区cke_combo_text与下拉箭头区cke_combo_open/cke_combo_arrow:

+-- .cke_combo ----------------------------------------------------------------------+ | +-- .cke_combo_label --+ +-- .cke_combo_button ----------------------------------+ | | | | | +-- .cke_combo_text --+ +-- .cke_combo_open --------+ | | | | | | | | | +-- .cke_combo_arrow --+ | | | | | | | +---------------------+ +------------------------+ | | | | +----------------------+ +------------------------------------------------------+ | +-----------------------------------------------------------------------------------+

上面的结构示意图来自 skins/moono-lisa/richcombo.css 文件头的注释。值得注意的是,cke_combo_label(标签)在视觉上是隐藏的,但保留用于无障碍语义(aria-labelledby引用它),见 plugins/richcombo/plugin.js。而悬停/按下样式的"舞台"全部集中在a.cke_combo_button上。

五、源码级原理(二):皮肤 CSS 中的三态视觉定义

测试文档期望的"悬停背景变深、点击后按下状态背景变浅",在皮肤样式中有精确对应的实现。skins/moono-lisa/richcombo.css 定义了 Combo 的两种状态类:

/* 按下态(on):浅色背景 #fff,深色边框 */ .cke_combo_on a.cke_combo_button, .cke_combo_off a.cke_combo_button:active { background: #fff; } /* 悬停/聚焦/激活态(off + hover):较深背景 #e5e5e5 */ .cke_combo_on a.cke_combo_button, .cke_combo_off a.cke_combo_button:hover, .cke_combo_off a.cke_combo_button:focus, .cke_combo_off a.cke_combo_button:active { background: #e5e5e5; border: 1px solid #bcbcbc; /* 通过负 margin 让边框覆盖相邻分隔线 */ padding: 0 0 0 1px; margin-left: -1px; }

对照测试步骤可以一一对应:

  • 步骤 1 的"悬停样式(背景变深)"即a.cke_combo_button:hover时的background: #e5e5e5(相对默认背景更深的灰色);
  • 步骤 2 的"点击激活 toggled on 状态(背景变浅)"即.cke_combo_on类生效时background: #fff(白色/更浅);
  • 两套规则中border: 1px solid #bcbcbc与margin-left: -1px的组合,是为了让高亮边框恰好覆盖 Combo 之间的垂直分隔线,视觉上形成连续的按下效果。该文件还对 RTL 布局(.cke_rtl)和高对比度模式(.cke_hc,border: 3px solid #000)分别做了对称处理(见 richcombo.css)。

关键:hover: none媒体查询正是 #4611 的修复点

同一个文件中,紧接状态规则之后有一段带注释的媒体查询(richcombo.css):

/* Disable hover styles on mobile devices (#4611). */ @media screen and ( hover: none ) { .cke_combo_off a.cke_combo_button:hover { background: transparent; border-color: transparent; } /* We need to restore :active styles. */ .cke_combo_off a.cke_combo_button:active { background: #fff; border: 1px solid #bcbcbc; } }

这正是 Bug #4611 的修复核心:在hover: none(触摸屏)环境下禁用:hover样式,同时保留:active点击反馈。桌面端测试文档要求"必须有悬停样式",是因为桌面设备hover: none不成立,悬停样式必须照常工作。

此外,皮肤脚本 skins/moono-lisa/skin.js 通过模板字符串将 Combo 悬停/按下颜色与主题色板绑定:

'{id} a.cke_combo_button:hover,' + '{id} a.cke_combo_button:focus,' + '{id} .cke_combo_on a.cke_combo_button [' + 'border-color:{toolbarElementsBorder};' + 'background-color:{darkBackground};' + '] '

这说明悬停/按下样式不只是写死的颜色,而是随皮肤主题色(darkBackground、toolbarElementsBorder)联动,这也是测试需要按 Moono-Lisa、Moono、Kama 三套皮肤分别验证的原因。

六、源码级原理(三):Rich Combo 的 TRISTATE 状态机

CSS 类cke_combo_on/cke_combo_off由 Rich Combo 组件的setState()方法控制,实现在 plugins/richcombo/plugin.js:

setState: function( state ) { if ( this._.state == state ) return; var el = this.document.getById( 'cke_' + this.id ), linkEl = el.getElementsByTag( 'a' ).getItem( 0 ); el.setState( state, 'cke_combo' ); state == CKEDITOR.TRISTATE_DISABLED ? el.setAttribute( 'aria-disabled', true ) : el.removeAttribute( 'aria-disabled' ); if ( linkEl ) { linkEl.setAttribute( 'aria-expanded', state == CKEDITOR.TRISTATE_ON ); } this._.state = state; }

这里的关键调用链是:

  • el.setState( state, 'cke_combo' )会依据CKEDITOR.TRISTATE_ON / OFF / DISABLED在容器上设置cke_combo_on/cke_combo_off/cke_combo_disabled类,从而触发第五节中的皮肤规则;
  • 同时同步维护无障碍属性:禁用时设置aria-disabled="true",面板展开(TRISTATE_ON)时设置aria-expanded="true"。

按下(toggled on)状态在源码中的生命周期非常清晰:

  1. 用户点击a.cke_combo_button,触发clickFn(plugin.js)进入instance.execute();
  2. createPanel()创建浮层面板,面板onShow回调中执行me.setState( CKEDITOR.TRISTATE_ON )并置me._.on = 1(plugin.js)——此时 Combo 进入"按下态"(浅色背景),对应测试步骤 2;
  3. 面板隐藏时(选择某项或按 Esc),onHide回调将状态恢复为TRISTATE_OFF(plugin.js)——按下态解除;
  4. 另外,updateState()(plugin.js)监听activeFilterChange、mode、selectionChange、readOnly事件,负责把 Combo 切换到TRISTATE_DISABLED(如编辑器只读或当前模式不支持时),并优先保证 Combo 处于展开状态时不抢改状态。

值得注意的细节是execute()中的防重入逻辑(plugin.js):如果_.on为真(面板已打开),再次点击会直接panel.hide()收起面板,而不是重复展开。这与"点击一次变浅、再点一次恢复"的交互直觉一致。

七、测试中两个 Combo 的真实来源:stylescombo 与 format 插件

测试页加载了stylescombo与format两个插件,它们分别通过editor.ui.addRichCombo()注册了名为Styles与Format的 Rich Combo:

  • plugins/stylescombo/plugin.js:editor.ui.addRichCombo( 'Styles', { ... panel: { multiSelect: true, ... } } ),样式列表按块级/内联/对象类型分组;
  • plugins/format/plugin.js:editor.ui.addRichCombo( 'Format', { ... panel: { multiSelect: false, ... } } ),格式列表(段落、标题 H1–H6、预格式化等)逐项添加。

两者在init回调中通过this.add( value, previewHtml, label )填充列表项(如 stylescombo/plugin.js),并在onClick中执行editor.applyStyle()/removeStyle()。因此测试者在页面上看到的"Rich Combo"就是这两个真实业务组件,验证的是用户实际会遇到的交互路径,而非抽象测试桩。

八、如何运行与验收这套手动测试

该测试是 Bender.js 手动测试体系的一部分,相关运行配置见仓库根目录 bender.js(其中manualBrowsers定义了接受手动测试的浏览器列表)。验收流程建议如下:

  1. 在桌面浏览器中打开由 Bender 服务器提供的手动测试页面(对应desktoprichcombo条目);
  2. 将鼠标移入第一个编辑器的 Styles / Format 组合框,确认背景变为较深的灰色(#e5e5e5悬停样式);
  3. 单击组合框展开下拉面板,确认按钮呈现较浅的按下态(#fff背景 +#bcbcbc边框);
  4. 关闭面板后,对第二个(Div 编辑器)重复第 2、3 步;
  5. 如需覆盖其他皮肤,切换至desktoprichcombomoono(Moono,另需验证按下态上的附加悬停阴影)与desktoprichcombokama(Kama);
  6. 若在 Android / iOS 真机上验证 #4611 修复本身,则使用对应的androidrichcombo*.md/iosrichcombo*.md变体——模板中的isIgnoredEnv()会自动忽略与当前设备不匹配的测试。

判定标准即测试文档中的 Expected result:悬停样式存在、点击后按下态存在,且两个编辑器表现一致;任一缺失即为 Unexpected result,需要回归检查 skins/moono-lisa/richcombo.css 中的状态规则是否被hover: none或其他皮肤覆盖逻辑误伤。

九、小结

desktoprichcombo.md虽然只是一份寥寥数步的手动测试说明,但它精确刻画了 CKEditor 4 工具栏 Rich Combo 的交互状态契约:桌面端悬停必须呈现深色反馈、点击展开必须呈现浅色按下态,且 Iframe / Div 两种编辑模式行为一致。其背后是plugins/richcombo的 TRISTATE 状态机(cke_combo_on/cke_combo_off/ disabled 三态切换 + ARIA 同步)与皮肤 CSS 中:hover、:active、.cke_combo_on规则的协同,而 #4611 的修复则以@media screen and ( hover: none )精确隔离了移动端的伪悬停问题。读懂这份测试文档与对应源码,即可完整掌握该交互的验收口径与实现机理。

  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载

相关推荐

上一篇:Spaceship Prompt Ruby 版本号提示段(ruby section)完整指南:配置、触发逻辑与版本管理器兼容原理
下一篇:CAQL 连续动作 Q-Learning 实践指南:面向 max-Q 问题的可插拔优化器设计与源码解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 6:38:19

wiliwili 完整安装指南:掌机与 PC 通吃的手柄 B 站客户端

wiliwili 完整安装指南:掌机与 PC 通吃的手柄 B 站客户端 【免费下载链接】wiliwili 第三方B站客户端,目前可以运行在PC全平台、PSVita、PS4 、Xbox 和 Nintendo Switch上 项目地址: https://gitcode.com/GitHub_Trending/wi/wiliwili wiliwili 是…

作者头像 李华
网站建设 2026/10/17 6:37:30

MIRIX: Multi-Agent Memory System for LLM-Based Agents

文章主要内容总结 本文介绍了一种名为MIRIX的多智能体记忆系统,旨在解决基于大语言模型(LLM)的智能体在记忆能力上的局限性。现有记忆系统多依赖扁平、范围狭窄的记忆组件,难以实现个性化、抽象化及长期可靠的用户信息召回,而MIRIX通过模块化设计和多智能体框架,重新定义…

作者头像 李华
网站建设 2026/10/17 6:37:13

MAME算法的各种密码分析方法全面盘点

MAME算法的各种密码分析方法全面盘点针对MAME(一种面向硬件高效实现的压缩函数)的密码分析,学术界已进行了多角度的评估。这些分析主要围绕其广义Feistel结构和MMO(Matyas-Meyer-Oseas)哈希模式展开,揭示了…

作者头像 李华
网站建设 2026/10/17 6:36:13

天若OCR实操指南:从截图到文字提取的完整配置与避坑经验

最近几年我这台办公机上的“截图转文字”需求越来越频繁,最初是处理扫描版合同和PDF里的文字段落,后来连聊天记录截图、网页里不让复制的段落、外文资料里的整页内容都得靠它搞定。中间折腾过不少工具,最后一直留着的反而是这款老牌截图OCR工…

作者头像 李华