fix: unify chat process and reply presentation

This commit is contained in:
2026-10-29 17:54:34 +08:00
parent 7621bf0f86
commit 9474788db6
46 changed files with 3160 additions and 500 deletions
@@ -0,0 +1,51 @@
import { act, renderHook } from "@testing-library/react";
import { afterEach, expect, it, vi } from "vitest";
import { useAgentConnection } from "./use-agent-connection";
afterEach(() => vi.useRealTimers());
it("rechecks an offline service, probes on focus and clears work on unmount", async () => {
vi.useFakeTimers();
const getHealth = vi.fn().mockRejectedValueOnce(new Error("offline")).mockResolvedValue(undefined);
const client = { getHealth };
const { result, unmount } = renderHook(() => useAgentConnection(client, "project-a"));
await act(async () => {});
expect(result.current.connectionStatus).toBe("offline");
await act(async () => vi.advanceTimersByTimeAsync(1_000));
expect(result.current.connectionStatus).toBe("online");
expect(result.current.recoveryVersion).toBe(1);
await act(async () => window.dispatchEvent(new Event("focus")));
expect(getHealth).toHaveBeenCalledTimes(3);
unmount();
expect(vi.getTimerCount()).toBe(0);
});
it("discards a late result from a previous client and scope", async () => {
vi.useFakeTimers();
let finish!: () => void;
let oldSignal!: AbortSignal;
const oldClient = { getHealth: vi.fn((signal: AbortSignal) => { oldSignal = signal; return new Promise<void>((resolve) => { finish = resolve; }); }) };
const nextClient = { getHealth: vi.fn(async () => { throw new Error("next service offline"); }) };
const { result, rerender, unmount } = renderHook(({ client, scope }) => useAgentConnection(client, scope), { initialProps: { client: oldClient, scope: "old" } });
await act(async () => {});
rerender({ client: nextClient, scope: "next" });
await act(async () => {});
expect(oldSignal.aborted).toBe(true);
await act(async () => finish());
expect(result.current.connectionStatus).toBe("offline");
unmount();
});
it("aborts a stalled probe and recovers on the next retry", async () => {
vi.useFakeTimers();
const getHealth = vi.fn().mockImplementationOnce((signal: AbortSignal) => new Promise<void>((_resolve, reject) => {
signal.addEventListener("abort", () => reject(new DOMException("timeout", "AbortError")), { once: true });
})).mockResolvedValue(undefined);
const client = { getHealth };
const { result, unmount } = renderHook(() => useAgentConnection(client, "project"));
await act(async () => vi.advanceTimersByTimeAsync(5_000));
expect(result.current.connectionStatus).toBe("offline");
await act(async () => vi.advanceTimersByTimeAsync(1_000));
expect(result.current.connectionStatus).toBe("online");
unmount();
});