fix(documents): surface timeline density fetch failures via console.warn (#385)

Previously a 5xx, network blip, or JSON parse error all collapsed
into the same silent "no buckets" rendering. The widget still
degrades gracefully — failure should not block the document list —
but operators and Sentry now see the failure in browser devtools
instead of having to reverse-engineer a missing chart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-05-08 10:55:04 +02:00
parent c9be6cc165
commit 2e9ce8e1da
2 changed files with 29 additions and 2 deletions

View File

@@ -285,6 +285,29 @@ describe('fetchDensity', () => {
expect(result).toEqual({ density: [], minDate: null, maxDate: null });
});
it('emits console.warn with the status when the response is non-ok', async () => {
const fetch = vi.fn().mockResolvedValue({ ok: false, status: 503 });
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
await fetchDensity(fetch, null, true);
expect(warn).toHaveBeenCalledTimes(1);
expect(warn.mock.calls[0][0]).toContain('503');
warn.mockRestore();
});
it('emits console.warn with the caught error when fetch rejects', async () => {
const error = new TypeError('Network down');
const fetch = vi.fn().mockRejectedValue(error);
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
await fetchDensity(fetch, null, true);
expect(warn).toHaveBeenCalledTimes(1);
expect(warn.mock.calls[0]).toContain(error);
warn.mockRestore();
});
});
describe('tickIndicesFor', () => {

View File

@@ -208,14 +208,18 @@ export async function fetchDensity(
try {
const response = await fetch(buildDensityUrl(filters));
if (!response.ok) return EMPTY;
if (!response.ok) {
console.warn(`[timeline] density fetch responded with ${response.status}`);
return EMPTY;
}
const body = (await response.json()) as DocumentDensityResult;
return {
density: body.buckets,
minDate: body.minDate ?? null,
maxDate: body.maxDate ?? null
};
} catch {
} catch (error) {
console.warn('[timeline] density fetch failed', error);
return EMPTY;
}
}