Skip to content

14 · 测试策略与维护方法

这个项目的测试重点不是组件快照,而是模板语言、过滤器、内容转换与高亮恢复等“输入空间大、回归隐蔽”的纯逻辑。

一、测试版图

快照中 *.test.ts 约 4,696 行。最大测试:

测试约行数关注点
renderer.test.ts566AST 求值、scope、控制流、filters
shared.test.ts455variables/frontmatter/selector/format
parser.test.ts444AST 与错误恢复
tokenizer.test.ts382词法边界与位置
highlights-manager.test.ts253高亮存储管理
template-integration.test.ts160端到端模板编译
highlighter-overlays.test.ts151quote/overlay 恢复
content-extractor.test.ts49内容与高亮注入

此外多数复杂过滤器有自己的测试文件。

二、Vitest 环境

vitest.config.ts 配置 jsdom,使 DOMParser、document、Element、Range 等浏览器概念可在 Node 测试。

src/utils/__mocks__/webextension-polyfill.ts 提供 storage/tabs/runtime 等 mock,隔离真实浏览器。

适合 jsdom 的部分:

  • selector;
  • HTML/Markdown 处理;
  • text node/Range;
  • UI helper 的基本 DOM;
  • storage 消息逻辑(配合 mock)。

不适合只靠 jsdom 证明的部分:权限弹窗、service worker suspend、side panel、Safari native host、真实 clipboard 和布局几何。

三、DSL 三层测试

模板语言应分层定位故障。

Tokenizer tests

关注字符如何切 token:

  • 普通 text 与 {{/{%
  • quotes/escape/newline;
  • selector 中 :[]?
  • trim markers;
  • 数字/boolean/null;
  • operator 与 keyword;
  • line/column;
  • 未闭合字符串/tag。

Parser tests

关注 token 如何形成 AST:

  • precedence;
  • nested if/for;
  • elseif/else;
  • set/member;
  • filter args;
  • stop keyword;
  • variable/filter validation;
  • 相似名称建议。

Renderer tests

关注语义:

  • truthiness;
  • scope shadowing;
  • loop arrays/objects/JSON;
  • nullish;
  • contains;
  • async resolver;
  • deferred variables;
  • partial errors;
  • whitespace trim。

三层都测的收益是:失败时知道是“没认出 token”“AST 错”还是“执行错”。

四、集成 fixture

utils/fixtures/templatesfixtures/expected 包含:

text
minimal
edge-cases
schema-rich
goodreads
imdb
youtube

每组通常有 HTML、template JSON 与 expected Markdown。它们覆盖从抽取、schema、模板到 frontmatter 的完整行为。

Golden file 的价值:格式化回归会直接显示 diff。缺点:预期更新容易被机械接受,因此 review 时必须解释为什么输出变化。

五、过滤器测试模式

一个高质量 filter test 至少覆盖:

text
正常输入
空字符串/null-like
Unicode/CJK/emoji
带引号、冒号、pipe 的参数
错误参数 validator
JSON array/object 输入
与前后过滤器链组合

文件名过滤器还要覆盖 Windows/macOS/Linux 非法字符;日期过滤器要固定时区或 mock time,避免 CI 地区差异。

六、高亮测试的特殊难点

Range 测试不能只比较字符串。应构造:

  • 多个 text node;
  • 嵌套 inline 标签;
  • 重复 exact 文本;
  • whitespace 折叠;
  • DOM 包装变化后的 quote fallback;
  • 跨 block selection;
  • element media;
  • cache invalidation。

几何 overlay 测试需 mock getClientRects/getBoundingClientRect,并分别验证规划逻辑与真实浏览器渲染。

七、消息与 Storage 测试

WebExtension mock 应能记录:

text
runtime.sendMessage calls
tabs.sendMessage(tabId, payload)
storage.sync/local get/set
onChanged listeners
scripting injection order
permissions request

重点验证“消息协议”,不必把 background 整个加载成黑盒。action string 是跨 bundle API,拼写变化应像 public API 一样测试。

八、浏览器手工矩阵

发布前至少验证:

场景ChromeFirefoxSafari
popup clip
embedded/side panelside panelfallbackfallback
reader 原位
reader 独立 fetchpermissionnative fallback
text/element highlight
context menu/shortcut
clipboard/download
template import/export
local provider InterpreterCORSCORSCORS/native boundary

移动 Firefox、iOS/iPadOS Safari 还需触摸、高亮删除、viewport 与 keyboard 单独验证。

九、静态质量检查

当前 package scripts 主要暴露 tests/build/i18n scripts,没有统一 lint script,但仓库有 ESLint/TypeScript 配置。

修改后建议最小验证:

bash
npm test
npx tsc --noEmit
npm run build
npm run build:api
npm run build:cli
npm run check-strings

若只改某个 filter,可先跑对应 test,再跑全套。

十、Debug 模式

webpack 用 DEBUG_MODE 全局常量:development 为 true,production 为 false 并被 Terser dead-code elimination。

debugLog(scope, ...) 让开发时能观察抽取、filters、Interpreter、storage 等中间状态,生产不带大量日志。

调试复杂模板时建议记录:

text
原始模板
tokens / tokenizer errors
AST / parser errors
validation errors
variables keys
render output / deferred flag
post-process output

比只看最终 Markdown 更容易定位。

十一、修改任务的回归半径

修改必测范围
新变量shared + parser validation + renderer + integration
新过滤器filter unit + metadata validation + renderer chain
新 DSL 语法tokenizer + parser + renderer + formatter
高亮锚点highlighter + overlays + extractor + Reader
Reader DOMapply/restore + highlights + transcript + mobile
storage 字段old fixture migration + save/load + settings UI
message actionsender + background router + content receiver
manifest 权限三浏览器 build + real install
API/CLIapi integration + CLI stdin/file/fetch + build exports

十二、维护大控制器的方法

popup.tsreader.tshighlighter.ts 的修改:

  1. 先画状态转移,不直接移动代码;
  2. 找到唯一权威状态;
  3. 列出所有 observer/listener;
  4. 明确 apply 与 cleanup 对称性;
  5. 把纯变换抽成可测函数;
  6. 避免在多个 bundle 重复创建 mutable singleton;
  7. 用 generation/idempotency 防重复初始化;
  8. 真浏览器连续 toggle 5–10 次观察泄漏。

十三、性能回归点

重点 profile:

  • 大页面 fullHtml clone/clean;
  • Defuddle async timeout;
  • 大模板 tokenizer/parser;
  • highlights normalized text index;
  • MutationObserver 引发 overlay reposition;
  • highlights library 大列表 render;
  • Reader transcript 与 scroll listeners;
  • sync storage 高频写。

已有的 debounce/throttle/cache/version 应在重构后保留语义。

十四、本章检查点

  • 能按 tokenizer/parser/renderer 三层设计 DSL 测试;
  • 能解释 golden fixture 的价值与风险;
  • 能区分 jsdom 可测与真浏览器必测;
  • 能按修改类型估算回归半径;
  • 能列出大控制器的 listener/cleanup/idempotency 检查。

基于 Obsidian Web Clipper 1.7.1 源码快照的独立学习笔记