Summary
TestsReachingList turns a rejected fetch into null and then renders null exactly like a real "nothing reaches this file" answer. A 500, a network error, or a server that is down all show the sentence "No test in the repository calls into this file, and none imports it."
packages/ui/src/health/tests-reaching-list.tsx:47-58:
const { data, isLoading } = useSWR<TestsReachingFile | null>(
fetcher ? `tests-reaching:${cacheKey}:${filePath}` : null,
() => fetcher!(filePath).catch(() => null),
{ revalidateOnFocus: false },
);
...
if (!data || !data.reached || data.tests.length === 0) {
Mechanism
The .catch(() => null) swallows the error before SWR sees it, so error is never set and data is null. The empty-state branch tests !data together with !data.reached, so "we could not ask" and "we asked and nothing reaches it" take the same path. The comment above that branch explains why the second case is shown this way ("A file nothing reaches is the honest answer, not an error"). The first case is the error that comment separates it from.
The web host (packages/web/src/components/files/file-tests-panel.tsx) passes getTestsReaching, which rejects on any non-2xx response from /api/repos/{repo_id}/health/tests-reaching.
A related server-side note: the endpoint also returns its basis: "none" empty answer when the graph walk raises (packages/server/src/repowise/server/routers/code_health/coverage_routes.py:359). Its docstring says basis: "none" exists so "no test reaches this" stays distinguishable from "the graph had nothing to say". That path is worth looking at in the same change, but the UI conflation holds regardless of it.
Repro
A vitest render against origin/main:
render(
<TestsReachingList filePath="src/a.py" cacheKey="zz"
fetcher={async () => { throw new Error("HTTP 500"); }} />,
);
await waitFor(() =>
expect(screen.getByText(/No test in the repository calls into this file/)).toBeInTheDocument(),
);
This passes. The rendered text is:
Tests reaching this fileNo test in the repository calls into this file, and none imports it. That is a static reading of the dependency graph, ...
Impact
A transient failure tells the reader the file is untested. The Tests tab exists to answer that question, and "no test reaches this" is the answer most likely to be acted on (write tests, or treat the file as risky to change). Nothing on the page says the answer is missing.
Done looks like
- A rejected fetch renders a short "could not load which tests reach this file" state that is visibly different from the empty answer. Let SWR carry the error, or keep a distinct sentinel. Do not map it to
null.
reached: false still renders the existing sentence and its static-reading caveat unchanged.
- No fetcher still renders nothing.
Tests
In packages/ui/__tests__/health/inferred-tests.test.tsx:
- A fetcher that rejects does not render "No test in the repository calls into this file", and does render the error state.
- The existing "names the static limits when nothing reaches the file" test keeps passing.
Summary
TestsReachingListturns a rejected fetch intonulland then rendersnullexactly like a real "nothing reaches this file" answer. A 500, a network error, or a server that is down all show the sentence "No test in the repository calls into this file, and none imports it."packages/ui/src/health/tests-reaching-list.tsx:47-58:Mechanism
The
.catch(() => null)swallows the error before SWR sees it, soerroris never set anddataisnull. The empty-state branch tests!datatogether with!data.reached, so "we could not ask" and "we asked and nothing reaches it" take the same path. The comment above that branch explains why the second case is shown this way ("A file nothing reaches is the honest answer, not an error"). The first case is the error that comment separates it from.The web host (
packages/web/src/components/files/file-tests-panel.tsx) passesgetTestsReaching, which rejects on any non-2xx response from/api/repos/{repo_id}/health/tests-reaching.A related server-side note: the endpoint also returns its
basis: "none"empty answer when the graph walk raises (packages/server/src/repowise/server/routers/code_health/coverage_routes.py:359). Its docstring saysbasis: "none"exists so "no test reaches this" stays distinguishable from "the graph had nothing to say". That path is worth looking at in the same change, but the UI conflation holds regardless of it.Repro
A vitest render against
origin/main:This passes. The rendered text is:
Impact
A transient failure tells the reader the file is untested. The Tests tab exists to answer that question, and "no test reaches this" is the answer most likely to be acted on (write tests, or treat the file as risky to change). Nothing on the page says the answer is missing.
Done looks like
null.reached: falsestill renders the existing sentence and its static-reading caveat unchanged.Tests
In
packages/ui/__tests__/health/inferred-tests.test.tsx: