文档

teact

单测界的 React — 受控数据驱动快照框架(test + react)。

把 React 的不可约结构同构到测试:

Reactteact
UI = f(state)out = run(in)(被测纯函数 + 渲染器)
JSX(声明终态)fixture 的 out.txt
key(节点身份)case 目录名
reconcilerrun 改了 → 跑遍 in → diff 新旧 out
受控数据流diff 经 review,approve 才落盘——不像 jest -u 盲写

#用法

每个 __fixtures__/<suite>/<case>/ 目录是一个用例:

__fixtures__/
  tokenize/
    cjk-bigram/
      in.txt        ← 输入(任意 *.txt 字符串 / *.json 结构,文件名即 key)
      out.txt       ← 期望终态(手写,全等比对)
  applyEdit/
    replace/
      cur.txt old.txt new.txt   ← 多具名输入
      out.txt
import { 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 是字符串输入、*.jsonJSON.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                     # 丢弃所有 proposed

propose 模式仍跑 toBe 断言——CI 误设环境变量也照样红。框架永不在测试运行中直接写 out.txt,只产未跟踪的 .proposed 旁路文件,必须人 approve 才落盘。钉错无法静默发生。

out.txt.proposed 加进 .gitignore

#为什么不用 vitest -u

.snap 机器生成、人不可读;-u 盲写覆盖、无 review → 钉错静默发生。teact 的 out.txt 是手写可读的终态声明,回写受控(变更经人 approve 才生效,= React 单向数据流)。