test(client-react): give the package a test harness and pin realtime hook behavior (#4682) - #4692
Merged
Merged
Conversation
…hook behavior (#4682) `packages/client-react` shipped 8 public hooks with `build` and `typecheck` as its only scripts and zero test files. `tsc --noEmit` is structurally blind to what actually breaks in a hook: a dependency array is a value, not a type, so a missing entry, a missing cleanup and a callback that never fires all typecheck perfectly. #4678 was exactly that shape — `useAutoRefresh` ignored predicate writes, the case that dirties a list hardest, and no type noticed. Adds the workspace's first DOM test environment (jsdom + @testing-library/react, `environment: 'jsdom'`; every other package runs `node`) and 17 tests over the realtime hooks covering the three things the type checker cannot see: - re-subscription driven by the dependency array — changing `object` opens a subscription on the new name and releases the old one, an unrelated re-render churns nothing, and the hooks key on the primitive `options?.recordId` / `options?.packageId` rather than the options object's identity, so an equal-but-new object stays a no-op; - release on unmount, including `useAutoRefresh`, which holds two subscriptions; - delivery — events reach state and callbacks, and `useAutoRefresh` refetches on the per-record *and* the bulk stream (the #4678 regression pin). Every assertion was verified by sabotage rather than assumed: dropping the `object` dep (1 failure), deleting a cleanup (3) and reverting `useAutoRefresh` to the single-stream version (3) each turn the suite red; reverting turns it green again. CI needs no wiring — `Test Core` partitions by package off `turbo ls`, and the new `test` script puts client-react on shard 2 (verified with scripts/partition-test-shards.mjs). No runtime code changed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LYnZrTwXbrctB8E8HpJAPT
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 2, 2026
…ding them `check:type-check-coverage` failed on the first push, and it was right. The initial commit added `**/*.test.tsx` to tsconfig's `exclude` to match the `**/*.test.ts` entry every sibling package carries — but that entry is frozen DEBT the repo is migrating away from (#4311), not a convention to copy. The gate's own summary makes it explicit: 21 packages still exclude their tests, carrying 2243 frozen raw errors in TEST_DEBT. Excluding one more would have reported green over source `tsc` never read. Drops the test exclusion entirely, so client-react typechecks its own tests and stays out of that ledger. Doing so immediately surfaced a real gap: the `METADATA_EVENT` fixture was declared and never used, because `useMetadataSubscription` was covered for re-subscription and unmount but not for delivery. Adds that assertion rather than deleting the fixture — 18 tests now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LYnZrTwXbrctB8E8HpJAPT
os-zhuang
marked this pull request as ready for review
August 2, 2026 19:09
os-zhuang
pushed a commit
that referenced
this pull request
Aug 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4682
问题
packages/client-react导出 8 个公开 hook,而package.json里只有build和typecheck,src/下一个测试文件都没有。tsc --noEmit结构上就看不见 hook 真正会坏的地方——依赖数组是值,不是类型。少一项依赖、少一个 cleanup、回调根本没被调用,这三种缺陷全都能完美通过类型检查。#4678 就是这个形状:
useAutoRefresh对谓词写纹丝不动(而谓词写恰恰是把列表弄脏得最厉害的那种写入),类型系统全程没有察觉。改动
新增全工作区第一个 DOM 测试环境(
jsdom+@testing-library/react,environment: 'jsdom';其余每个包都是node),以及 17 条覆盖 realtime hooks 的测试,正对着类型检查看不见的三件事:1. 依赖数组驱动的重订阅 —— 改
object会在新名字上开订阅并释放旧的;无关的重渲染不产生任何 churn。还钉住了一点:hook 依赖的是原始值options?.recordId/options?.packageId,而不是 options 对象的身份,所以「值相等但身份是新的」对象必须是 no-op。把那个依赖「顺手简化」成options会让这条测试变红。2. 卸载时释放 —— 每个订阅 hook 都退订,包括持有两个订阅的
useAutoRefresh。另有一条:卸载后迟到的事件不得触发 state 更新(React 对这类缺陷是打 console.error 而非抛错,所以断言也盯着 console)。3. 事件确实送达 —— 事件进到 state 和回调,且
useAutoRefresh在逐记录流和批量流两条上都会 refetch(#4678 的回归钉)。断言不是空的:破坏验证
绿色本身不证明什么,所以每一类断言都用破坏源码的方式验过:
useDataSubscription依赖数组拿掉objectuseDataSubscription的 cleanupuseAutoRefresh退回只订逐记录流(#4678 原缺陷)CI 无需接线
Test Core是按包分片的(scripts/partition-test-shards.mjs读turbo ls),所以加一个test脚本就够了。已用该脚本实测确认 client-react 落在分片 2。顺带说明:那段分片注释提到的 "vitest 4 对测试文件数少于分片数的包硬失败" 不适用于本包——分片是按包切的,每个包整套跑在一个分片上。
验证
按真实退出码(注意
cmd | tail的退出码是tail的,不能当证据):tsconfig.json的 exclude 补了**/*.test.tsx—— 全仓统一把测试排除在tsc之外(**/*.test.ts),但那个 glob 匹配不到.tsx。没有改动任何运行时代码。
用这套基座立刻抓到的两个缺陷
基座建起来不到一小时就抓到两个 typecheck 永远看不见的真实缺陷,都已单独立案、不夹带进本 PR:
useQuery在文档示例用法下是无界请求循环(P0)。useQuery('todo_task', { where: … })在 300ms 内发出 8830 次find;把 options 提升成稳定对象则是 1 次。useAutoRefresh在回调身份不稳定时每渲染一次就重订阅一次。3 次渲染 → 每条流各 3 个订阅。两者同源(依赖数组按身份而非按值),且在同一条调用链上——
useAutoRefresh的 TSDoc 示例正是const { refetch } = useQuery(...)。都是实测数字,不是读代码推断的。修复各自独立,本 PR 只交付基座本身。🤖 Generated with Claude Code
https://claude.ai/code/session_01LYnZrTwXbrctB8E8HpJAPT