import { describe, expect, it } from "vitest"; // P1-6 回归守卫:学文风轮询完成(done 边沿)时,effect 必须用「最近一次 learn 传入的 // projectId」去拉指纹,而非闭包捕获的旧值。useStyleLearn 用 ref 追踪 projectId 来保证这点。 // // 该 hook 依赖浏览器/React 渲染,node 环境无法整体跑;这里以最小模型固化 ref 追踪契约: // 切项目(连续两次 learn)后,done 时读到的应是后者,不会是首次的陈旧值。 describe("useStyleLearn projectId 追踪(P1-6 stale-closure 守卫)", () => { // 复刻 hook 内部:learn 同步写 ref,done 时同步读 ref。 function makeTracker() { const projectIdRef: { current: string | null } = { current: null }; return { // learn(pid):受理时记录最近项目。 learn(pid: string): void { projectIdRef.current = pid; }, // onDone():轮询完成时取用于拉指纹的项目(应为最新)。 onDone(): string | null { return projectIdRef.current; }, }; } it("done 时用最近一次 learn 的 projectId(切项目后不复用旧值)", () => { // Arrange const t = makeTracker(); // Act:先 learn 项目 A,再切到项目 B(模拟用户换项目重学),此时 A 的轮询尚未完成。 t.learn("project-A"); t.learn("project-B"); const used = t.onDone(); // Assert:done 边沿读到的是 B,而非闭包里陈旧的 A。 expect(used).toBe("project-B"); }); it("未 learn 时为 null(不误拉指纹)", () => { const t = makeTracker(); expect(t.onDone()).toBeNull(); }); });