工具箱的结构很朴素:一个 tools.js,一个 TOOLS 数组,每个元素带 id / cat / name / build(),渲染循环逐个调用 build() 往网格里塞卡片。工具数量到 139 时,这个结构的问题开始显形:任何一个 build() 抛异常,后面的工具全部不渲染,而页面上没有任何提示 —— 用户只会看到「少了几个工具」。
第一步:把注册和渲染分开测
浏览器里跑测试成本太高,所以用了最土的办法:Node 里用 vm 加一套桩对象跑 tools.js。但整个文件跑不起来 —— 渲染段依赖真实布局(canvas 尺寸、IntersectionObserver、scrollIntoView)。于是把源码从 /* ---------------- 渲染 ---------------- */ 处截断,只求值注册段,再补一行把 TOOLS 导出。这样能验证「160 个工具全部注册成功、分类齐全、id 唯一」。
第二步:桩要够真,但不必真
桩元素给 classList、dataset、appendChild、querySelector,getContext() 返回一个 Proxy —— 任何画布方法都返回 {},measureText 返回 { width: 10 }。够让工具跑完初始化,又不需要真浏览器。
第三步:把错误分桶,别被环境噪音淹掉
头几轮跑出来一堆 Cannot convert undefined or null to object、is not a function,全当缺陷处理会跑偏。于是加了一条规则:命中「桩环境受限」特征(null 转换、方法缺失、未定义变量)就归类为环境问题,其余才算真缺陷。这条规则立刻让问题数从 40+ 降到 3。剩下三条里有一条是真的:单位换算工具在 <select> 取不到值时直接 Object.keys(undefined) 崩了 —— 浏览器里只要默认选项存在就不会触发,但它确实不够健壮。
这里定了一条原则:桩不够真是代码不健壮的信号,修工具,不修桩。于是改成 UNITS[catEl.value] || UNITS.len,并给 from 一个兜底。改完这一处,160 个工具全部通过,环境受限 0 个、真实问题 0 处。
第四步:单工具页要单独验
详情页用 <html data-tool="vlsm"> 让渲染循环只放行一个工具,逻辑很短但容易写错:ONLY 为空时(工具箱主页面)必须不拦截。所以又写了一个小测试,把 data-tool 桩成目标 id,断言「只渲染一张卡、卡 id 正确、没有抛错」,覆盖 9 个代表性工具(加密、时间、计算、表格、语音、子网、权限、图片、TOTP),9/9 通过。
为什么值得花这些时间
160 个工具是「广度」,但用户对站点的信任来自「点哪个都能用」。一次白屏或一个静默消失的工具,破坏的信任远大于多做的三十个工具带来的好感。测试不能保证每个工具算得对,但能保证没有工具因为别人的错误而消失 —— 这在一个人维护 160 个工具的时候,是最要紧的一条底线。
补记:写完这篇之后工具箱从 139 个涨到 160 个,加的还是同一套流程 —— 先写注册函数,再进桩环境跑一遍单工具模式。标题保留当时的样子。