Skip to content

finding(test-infra): 13 app-shell tests stub useRecordPresence as an object while the hook returns an array — the no-presence branch is covered by a shape mismatch #8083

Description

@claude

Surfaced while sweeping @object-ui/collaboration's frozen vi.mock factories for #6892 slice 4 (PR #8082). Filed unassigned. Deliberately not fixed there — that PR converts factories to inherit the real export surface, and changing a stub's value shape is a behaviour change it was not dispatched to make.

What was measured

useRecordPresence returns an array. packages/collaboration/src/PresenceProvider.tsx:101:

export function useRecordPresence(
  objectName: string | undefined,
  recordId: string | undefined,
): PresenceUser[] {

Its only consumer reads .length on that array — packages/app-shell/src/views/RecordDetailView.tsx:2358:

{recordPresence.length > 0 && (
  <PresenceAvatars users={recordPresence} size="sm" maxVisible={3} showStatus />
)}

Across the 22 packages/app-shell test files that stub useRecordPresence, the stubs disagree with each other and 13 of them disagree with the hook:

stub files matches the real return type
useRecordPresence: () = arrow [] 9 ✅ yes
useRecordPresence: () = arrow ({ viewers: [], others: [] }) 13 ❌ no — the hook never returns an object

Why it is a defect and not a style nit

The 13 tests pass, and they pass for the wrong reason. An object has no length, so recordPresence.length is undefined, undefined > 0 is false, and the presence row is never rendered. Those 13 files therefore exercise the "no presence" branch through a shape mismatch, not through the empty array the branch is actually about. Two consequences:

  1. They cannot fail in the direction they look like they cover. No value of the { viewers, others } stub can ever make the row render, so nothing in those files pins the row-visible path even accidentally.
  2. Nothing catches the divergence. A vi.mock factory is untyped, so tsc never compares the stub against PresenceUser[]; the drift is invisible to every gate in the tree. If the hook's contract changes, the 9 correct files move and the 13 stay green.

The { viewers, others } shape appears to be borrowed from a different presence surface — no export of @object-ui/collaboration returns it.

Scope

Test-only. packages/app-shell/src/views/RecordDetailView.*.test.tsx, 13 files. The repair is presumably to make the 13 stubs return an array like the other 9, then check whether any assertion in them was silently relying on the row being unrenderable — that second half is the actual work, and it is why this is a card rather than a rider on the sweep.

Related: #6892 (the sweep worklist whose slice 4 surfaced this). The sweep's conversion leaves each override exactly as it was, so this PR neither introduces nor repairs the mismatch.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions