Files
fusion/packages/dashboard/app/hooks/__tests__/usePrChecksStream.resume-instrumentation.test.ts
Fusion (runfusion.ai) 854045f430 feat(FN-5416): instrument SSE stream resume events across PR checks, dev-se
Adds structured stream-resume instrumentation to the dashboard across four SSE hooks (PR checks, dev-server logs, research, background sessions), wiring `[wake-trigger-diagnostics]` log markers and view-resume route guards so the engine can distinguish cold starts from resume paths; includes tests f

Fusion-Task-Id: FN-5416
2026-05-21 18:15:26 -07:00

113 lines
3.4 KiB
TypeScript

import { act, renderHook, waitFor } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const recordResumeEvent = vi.fn();
vi.mock("../../utils/resumeInstrumentation", () => ({
recordResumeEvent,
}));
vi.mock("../../api", () => ({
fetchPrChecks: vi.fn(),
}));
async function flushPromises(): Promise<void> {
await Promise.resolve();
await Promise.resolve();
}
describe("usePrChecksStream resume instrumentation", () => {
beforeEach(() => {
recordResumeEvent.mockReset();
});
afterEach(() => {
vi.useRealTimers();
});
it("emits remount and visibility resume events with task detail", async () => {
const { fetchPrChecks } = await import("../../api");
const mockFetchPrChecks = vi.mocked(fetchPrChecks);
mockFetchPrChecks.mockResolvedValue({ checks: [], rollup: "pending", lastCheckedAt: "" });
const { usePrChecksStream } = await import("../usePrChecksStream");
renderHook(() => usePrChecksStream({ taskId: "FN-1", projectId: "proj-1", prNumber: 42, enabled: true }));
await waitFor(() => {
expect(recordResumeEvent).toHaveBeenCalledWith(expect.objectContaining({
view: "usePrChecksStream",
trigger: "remount",
detail: { taskId: "FN-1", prNumber: 42 },
}));
});
Object.defineProperty(document, "hidden", { configurable: true, value: true });
act(() => {
document.dispatchEvent(new Event("visibilitychange"));
});
Object.defineProperty(document, "hidden", { configurable: true, value: false });
act(() => {
document.dispatchEvent(new Event("visibilitychange"));
});
expect(recordResumeEvent).toHaveBeenCalledWith(expect.objectContaining({
view: "usePrChecksStream",
trigger: "visibility",
reason: "hidden",
detail: { taskId: "FN-1", prNumber: 42 },
}));
expect(recordResumeEvent).toHaveBeenCalledWith(expect.objectContaining({
view: "usePrChecksStream",
trigger: "visibility",
reason: "visible-resume",
detail: { taskId: "FN-1", prNumber: 42 },
}));
});
it("keeps stable-check polling backoff behavior", async () => {
vi.useFakeTimers();
const { fetchPrChecks } = await import("../../api");
const mockFetchPrChecks = vi.mocked(fetchPrChecks);
mockFetchPrChecks.mockResolvedValue({
checks: [{ name: "ci", state: "success" }],
rollup: "pending",
lastCheckedAt: "",
});
const { usePrChecksStream } = await import("../usePrChecksStream");
renderHook(() => usePrChecksStream({ taskId: "FN-1", projectId: "proj-1", prNumber: 42, enabled: true }));
await act(async () => {
await flushPromises();
});
mockFetchPrChecks.mockClear();
await act(async () => {
vi.advanceTimersByTime(15_000);
await flushPromises();
});
await act(async () => {
vi.advanceTimersByTime(15_000);
await flushPromises();
});
await act(async () => {
vi.advanceTimersByTime(15_000);
await flushPromises();
});
expect(mockFetchPrChecks).toHaveBeenCalledTimes(3);
await act(async () => {
vi.advanceTimersByTime(59_000);
await flushPromises();
});
expect(mockFetchPrChecks).toHaveBeenCalledTimes(3);
await act(async () => {
vi.advanceTimersByTime(1_000);
await flushPromises();
});
expect(mockFetchPrChecks).toHaveBeenCalledTimes(4);
});
});