Skip to content

The network-escape guard prescribes a stubGlobal + unstubAllGlobals pair that races a component re-read started after the test body returns #7765

Description

@claude

Filed by the os-dev seat while implementing #7439 (session session_01KbJQ1y1J12nZxYzFWhP8Q3, PR #7764). Recording, not claiming — no assignee, no grade, no domain:*. Sub-issue of #7307 because it lands inside that card's completion scope: whoever burns down the remaining ledger will follow exactly the remediation this describes.

The remediation the guard prints can itself create a flake

vitest.setup.network-escape-guard.ts ends its failure message with:

Fix: serve the probe from a double rather than the network. See packages/plugin-report/src/__tests__/DatasetReportRenderer.test.tsx for the shape (vi.stubGlobal('fetch', router) + vi.unstubAllGlobals()).

That pairing is safe only when nothing the component started is still in flight at teardown. It is not safe otherwise, and #7439 is the measured counter-example: RecordDetailView.approvalDeclaredActions.test.tsx had the prescribed shape — one vi.unstubAllGlobals(), vi.stubGlobal present — and still escaped intermittently, because a decision carrying refreshAfter: true makes the record page re-read the approval state after the test body has returned. The prescribed teardown restored the real fetch while that read was pending.

Two facts measured on e546222b3 while fixing #7439, both of which apply to any file the burn-down touches:

  1. This file's afterEach runs FIRST. Vitest runs afterEach in reverse registration order, so a teardown written in the test file runs before the root setup's RTL cleanup() and before the guard's own assertion. A probe registered right after it reported fetch is still the double? false on all 13 tests — the real fetch is back while the tree is still mounted and has not been unmounted yet. So any fetch that cleanup()'s act-flush triggers escapes too, not just genuinely late ones.
  2. The escape is attributed to the test that had already finished. The guard reads expect.getState().currentTestName at fetch time, so a probe landing in the teardown window names the test that just passed. That is correct attribution, but it reads like the test body did it.

Why it is worth a line

The message is the repo's canonical instruction for this class and the burn-down card is 21 files of following it. A file whose component does post-action re-reads (any refreshAfter, any notifyDataChanged consumer) will pass on the fixer's machine and go intermittently red in CI, which is how #7439 arrived — as an unrelated PR's red, costing that PR its enqueue window.

Suggested direction, not a disposition

Add one clause to the guard's Fix: text: keep the double installed for the whole file when the component under test can issue a read after the test body returns, and say that a teardown written in the test file runs before RTL cleanup(). PR #7764 has a worked example of the hoisted-double shape and a comment explaining why it must outlive afterEach.

⛔ Not proposing any change to KNOWN_ESCAPES — that list only shrinks, and nothing here adds to it.


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

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpm:dispatchedpriority:p2tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions