teactjs
v0.1.0
Published
单测界的 React — 受控数据驱动快照框架:声明终态,diff 经 review 才落盘
Maintainers
Readme
teact
单测界的 React — 受控数据驱动快照框架(test + react)。
把 React 的不可约结构同构到测试:
| React | teact |
|---|---|
| UI = f(state) | out = run(in)(被测纯函数 + 渲染器) |
| JSX(声明终态) | fixture 的 out.txt |
| key(节点身份) | case 目录名 |
| reconciler | run 改了 → 跑遍 in → diff 新旧 out |
| 受控数据流 | diff 经 review,approve 才落盘——不像 jest -u 盲写 |
工具全景
teact 不是单一断言器,是一组测试形态 —— 同一条命脉(受控回写 + 边界优先 + 纯函数核)投影到前端高频场景。按需升级、零破坏共存:
| 形态 | 钉什么 | 入口 | 文档 |
|---|---|---|---|
| fixture | 具体 in → 具体 out(回归基底) | snapshotSuite('name', run) | 本页 |
| pipe-trace | 管道每一跳的中段产物 | snapshotSuite('name', pipe(...)) | 本页 |
| law | 对所有输入成立的不变式(性质) | law('name', fn, [...]) | law 与 roundtrip |
| roundtrip | decode∘encode = id(双射失真) | roundtrip('name', enc, dec) | law 与 roundtrip |
| clock | debounce/throttle/退避/轮询的时序 | clock('name').tick(ms).expectCalls(n) | 时间与非确定性 |
| seeded | 钉住 Math.random/Date.now/crypto | seeded(seed, fn) | 时间与非确定性 |
| machine | 状态机可达性/死态/非法迁移 | machineSuite('name', {testDir}) | 领域骑手 |
| graph / dom | 图可达性 / DOM 命中决策 | teact/graph、teact/dom 子路径 | 领域骑手 |
| 变异自愈 | 测试漏掉的 behavior → 自动补 case | teact mutate <fn> --heal <dir> | 变异自愈 |
边界优先是灵魂:law/clock/roundtrip 的输入从类型反推、先穷举魔鬼值(
0/''/NaN/MAX/[]/😀/__proto__/纪元0/无效Date)。bug 99% 藏在那里。
用法
每个 __fixtures__/<suite>/<case>/ 目录是一个用例:
__fixtures__/
tokenize/
cjk-bigram/
in.txt ← 输入(任意 *.txt 字符串 / *.json 结构,文件名即 key)
out.txt ← 期望终态(手写,全等比对)
applyEdit/
replace/
cur.txt old.txt new.txt ← 多具名输入
out.txtimport { createSuite } from 'teact'
const snapshotSuite = createSuite(import.meta.dirname) // → <testDir>/__fixtures__
// 单输入:input 直接是那个值
snapshotSuite('tokenize', (input: string) => tokenize(input).join('\n'))
// 多具名:用 ctx.inputs
snapshotSuite('applyEdit', (_in, _opts, { inputs }) =>
applyEdit(inputs.cur as string, inputs.new as string, inputs.old as string))
// 带 opts.json
snapshotSuite('compactCss', (input: string, opts) =>
compactCss(input, (opts as { filename: string }).filename))输入约定:一个 case 目录里除 out.txt/opts.json 外,每个 *.txt 是字符串输入、*.json 是 JSON.parse 结构输入,key = 去扩展名的文件名。恰好一个输入文件时 input 直接给那个值;多个时 input === ctx.inputs。
受控回写:propose → review → approve
改了被测函数或渲染器、或加新 case 缺 out.txt 时:
UPDATE_SNAPSHOTS=1 vitest run # propose:不碰 out.txt,产 out.txt.proposed + 打彩色 diff
teact status # review:列出所有待审变更
teact approve [pattern] # 落盘:out.txt.proposed → out.txt
teact clean # 丢弃所有 proposedpropose 模式仍跑 toBe 断言——CI 误设环境变量也照样红。框架永不在测试运行中直接写 out.txt,只产未跟踪的 .proposed 旁路文件,必须人 approve 才落盘。钉错无法静默发生。
把 out.txt.proposed 加进 .gitignore。
为什么不用 vitest -u
.snap 机器生成、人不可读;-u 盲写覆盖、无 review → 钉错静默发生。teact 的 out.txt 是手写可读的终态声明,回写受控(变更经人 approve 才生效,= React 单向数据流)。
