Improve metrics clarity, exploration, and refresh reliability

This commit is contained in:
2026-09-27 22:12:11 -04:00
parent 981fb27c1c
commit 3922e28ca8
15 changed files with 984 additions and 135 deletions
+68
View File
@@ -0,0 +1,68 @@
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createMetricsRefresh } from '../../src/lib/ui/metrics-refresh.js';
afterEach(() => vi.useRealTimers());
const report = (days: number) => ({ days, summary: {}, points: [], maps: [], servers: [] });
function setup(fetcher: typeof fetch) {
let query = 'days=7';
const onData = vi.fn(), onError = vi.fn(), onLoading = vi.fn();
return { ...createMetricsRefresh({ getQuery: () => query, onData, onError, onLoading, fetcher, timeoutMs: 100 }),
onData, onError, onLoading, setQuery: (value: string) => { query = value; } };
}
describe('metrics background refresh', () => {
it('keeps the previous report after failure and accepts a successful retry', async () => {
const fetcher = vi.fn().mockResolvedValueOnce(new Response('', { status: 503 }))
.mockResolvedValueOnce(Response.json(report(7)));
const refresh = setup(fetcher);
await refresh.refresh();
expect(refresh.onData).not.toHaveBeenCalled();
expect(refresh.onError).toHaveBeenLastCalledWith('Report refresh failed (503).');
await refresh.refresh();
expect(refresh.onData).toHaveBeenLastCalledWith(report(7));
expect(refresh.onError).toHaveBeenLastCalledWith(null);
expect(refresh.onLoading).toHaveBeenLastCalledWith(false);
});
it('coalesces duplicate requests and ignores old replies even when transport ignores abort', async () => {
const resolve: ((r: Response) => void)[] = [];
const fetcher = vi.fn(() => new Promise<Response>(done => resolve.push(done)));
const refresh = setup(fetcher);
const old = refresh.refresh();
expect(refresh.refresh()).toBe(old);
expect(fetcher).toHaveBeenCalledTimes(1);
refresh.setQuery('days=1');
const next = refresh.refresh();
resolve[1](Response.json(report(1)));
await next;
resolve[0](Response.json(report(7)));
await old;
expect(refresh.onData).toHaveBeenCalledTimes(1);
expect(refresh.onData).toHaveBeenLastCalledWith(report(1));
});
it('suppresses errors from a cancelled request', async () => {
let reject!: (error: Error) => void;
const refresh = setup(vi.fn(() => new Promise<Response>((_, fail) => { reject = fail; })));
const pending = refresh.refresh();
refresh.cancel();
reject(new Error('Obsolete response'));
await pending;
expect(refresh.onData).not.toHaveBeenCalled();
expect(refresh.onError.mock.calls).toEqual([[null]]);
expect(refresh.onLoading).toHaveBeenLastCalledWith(false);
});
it('times out with retry feedback and clears loading', async () => {
vi.useFakeTimers();
const fetcher = vi.fn((_input, init) => new Promise<Response>((_, reject) => {
init?.signal?.addEventListener('abort', () => reject(new Error('aborted')));
})) as typeof fetch;
const refresh = setup(fetcher);
const pending = refresh.refresh();
await vi.advanceTimersByTimeAsync(100);
await pending;
expect(refresh.onError).toHaveBeenLastCalledWith('Report refresh timed out. Try again.');
expect(refresh.onLoading).toHaveBeenLastCalledWith(false);
});
});