# 测试报告 · 2026-10-16 发布前的全面测试。**离线 35 项 + 真机 11 项全过,零 pageerror**;过程中抓出并修掉 2 个真缺陷(都在真机路径上,离线测不出来)。 ## 一、离线(`npm test`,0 网络 0 额度) | 文件 | 项数 | 覆盖面 | |---|---|---| | `test/selftest.mjs` | 9 | 排序打分、模型 id 解析、文件收集封顶与排除、workspace.json v2 形状、**词表命中(inp→input)**、语言别名归一、按语言上词表 | | `test/server-routes.mjs` | 6 | 两条路由注册形状、**信任栅栏**(loopback 放行 / 非本机 Host 403 / 跨站 Origin 403 / 坏 Origin 403)、方法闸 405、config.json 载荷(版本、词表规模、**出处 4 条且都带 repo+license+commit**、no-store)、words 路由的 lang 归一与公共池 | | `test/client-smoke.mjs` | 8 | bundle 头与导出形状、不 require 第三方、shouldPair/tokenBefore/keyAction 判据、docWords(本文词口径)、langHint(围栏识别) | | `test/client-behavior.mjs` | 12 | **假 DOM(TEXTAREA 形态)驱动真监听**:配对、标识符后不配对、环绕选区、跳闭合、删空对、Ctrl+Space 取数、Tab 接受替换 token、打两个字母自动弹且本文词优先、中文/数字不触发、Esc 关层、`/` 触发、围栏语言提示 | ## 二、真机(headless Chrome 打真 composer,Lexical contenteditable) 11 项全过,0 pageerror,跑完输入框已清空:空框打 `(` 配对成 `()`(caret 在中间)、闭符在右边时打 `)` 跳过去、 caret 夹在空对里 Backspace 删两侧、标识符后打 `(` 不配对、选中再打 `(` 环绕成 `(abc)` 且**选区落在 abc**、 打两个字母自动弹且本文词在前、Tab 接受把 `inp` 换成候选、Esc 关层、`/` 触发、中文不触发、收尾清空。 ## 三、抓出的两个真缺陷(已修) 1. **Lexical 不认 `sel.modify(...)`**。捕获阶段做的选区操作在真 composer 上静默失效:配对后的光标跑到闭符之后、 跳闭合失效、删空对只删一侧、环绕选区不还原、Tab 接受变成「insert 而不是 replace」(`ininputValue`)。 修法:把 composer 文本摊平成字符偏移,用 `createTreeWalker` + 显式 `Range` 定位(`flatText`/`caretOffset`/`setSelectionRange`), 删除路径也从 `sel.modify` 换过来。**离线测不出**——假 DOM 里 `sel.modify` 本来就不存在,文本路径(TEXTAREA)是另一条实现。 2. **捕获阶段早于宿主处理**,插入后立刻改选区会被 Lexical 随后的渲染覆盖。修法:contenteditable 这条路的选区调整 用 `soon()`(`setTimeout 0`)推一拍。 附带修掉一个文档漂移:「环绕选区后选区保持选中」原来只在 README 里成立(实现把 caret 停在闭符之后)。 ## 四、仍未验的(说清楚,不含糊) - **语言词表**(`inp` → `input` 这类)在服务端半边,实测改码不重读(现场 `config.json` 仍报旧版本号), 要等一次 `dsh web` 重启才全线生效;本文词那半(客户端)刷新页面即生效,已在真机验过。 - 老 `textarea` 形态 composer 只有离线覆盖(假 DOM),没有真机样本。