fix(diagnostics): say which read failed, and let Live restart

- "Load older" is not offered off a loading filter's placeholder rows,
  whose cursor belongs to the previous filter.
- A failed older page, stale rows and missing counts each get their own
  message (HU/EN).
- Live shows as off once a failed read stopped the polling; pressing it
  again reads now.
This commit is contained in:
2026-09-19 03:48:01 +02:00
parent 8f6f89832f
commit eb29f22eb7
4 changed files with 77 additions and 16 deletions
@@ -197,6 +197,44 @@ describe("AdminLogs", () => {
const reads = logsMock.mock.calls.length;
await act(async () => vi.advanceTimersByTimeAsync(12000));
expect(logsMock.mock.calls.length).toBe(reads);
// The button says polling stopped, and pressing it reads again.
const liveButton = screen.getByRole("button", { name: "adminLogs.live" });
expect(liveButton.getAttribute("aria-pressed")).toBe("false");
fireEvent.click(liveButton);
await settle();
expect(logsMock.mock.calls.length).toBe(reads + 1);
});
it("a failed older page says so, not that the list is out of date", async () => {
logsMock.mockImplementation(async (_q, before) => {
if (before !== null) throw new Error("500");
return { items: [row(2)], accounts: {}, next_cursor: "c1" };
});
renderConsole();
await waitFor(() => expect(lines()).toHaveLength(1));
fireEvent.click(screen.getByRole("button", { name: "adminLogs.older" }));
expect((await screen.findByRole("alert")).textContent).toContain("adminLogs.olderFailed");
});
it("offers no older page while a filter change loads", async () => {
let landErrors!: (page: LogPage) => void;
logsMock.mockImplementation((q) =>
q.levels
? new Promise<LogPage>((resolve) => (landErrors = resolve))
: Promise.resolve({ items: [row(2)], accounts: {}, next_cursor: "c1" }),
);
renderConsole();
await waitFor(() => expect(lines()).toHaveLength(1));
expect(screen.queryByRole("button", { name: "adminLogs.older" })).toBeTruthy();
fireEvent.click(screen.getByRole("button", { name: /settings\.myLog\.filters\.error/ }));
await waitFor(() => expect(landErrors).toBeTypeOf("function"));
// The rows on screen are the previous filter's: their cursor is not this list's.
expect(screen.queryByRole("button", { name: "adminLogs.older" })).toBeNull();
await act(async () => landErrors({ items: [row(1)], accounts: {}, next_cursor: null }));
});
it("copies every loaded line, oldest first", async () => {