Skip to content

Commit fde8745

Browse files
committed
test(ui): harden manual refresh helper test
1 parent a9f36fa commit fde8745

1 file changed

Lines changed: 46 additions & 39 deletions

File tree

ui/src/ui/app-render.helpers.node.test.ts

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -650,52 +650,59 @@ describe("resolveSessionOptionGroups", () => {
650650

651651
describe("handleChatManualRefresh", () => {
652652
it("waits for chat history before scrolling and clearing refresh state", async () => {
653-
let animationFrameCallback: FrameRequestCallback | null = null;
653+
const animationFrame = { callback: undefined as FrameRequestCallback | undefined };
654654
const previousRequestAnimationFrame = globalThis.requestAnimationFrame;
655655
Object.defineProperty(globalThis, "requestAnimationFrame", {
656656
configurable: true,
657657
value: vi.fn((callback: FrameRequestCallback) => {
658-
animationFrameCallback = callback;
658+
animationFrame.callback = callback;
659659
return 1;
660660
}),
661661
});
662-
let resolveRefresh!: () => void;
663-
refreshChatMock.mockReturnValueOnce(
664-
new Promise<void>((resolve) => {
665-
resolveRefresh = resolve;
666-
}),
667-
);
668-
const state = {
669-
chatManualRefreshInFlight: false,
670-
chatNewMessagesBelow: true,
671-
updateComplete: Promise.resolve(),
672-
resetToolStream: vi.fn(),
673-
scrollToBottom: vi.fn(),
674-
} as unknown as Parameters<typeof handleChatManualRefresh>[0];
675-
676-
const run = handleChatManualRefresh(state);
677-
await Promise.resolve();
678-
679-
expect(state.scrollToBottom).not.toHaveBeenCalled();
680-
resolveRefresh();
681-
await run;
682-
683-
expect(refreshChatMock).toHaveBeenCalledWith(state, {
684-
awaitHistory: true,
685-
scheduleScroll: false,
686-
});
687-
expect(state.scrollToBottom).toHaveBeenCalledWith({ smooth: true });
688-
expect(state.chatManualRefreshInFlight).toBe(true);
689-
expect(animationFrameCallback).toBeTypeOf("function");
690-
691-
animationFrameCallback?.(0);
692-
693-
expect(state.chatManualRefreshInFlight).toBe(false);
694-
expect(state.chatNewMessagesBelow).toBe(false);
695-
Object.defineProperty(globalThis, "requestAnimationFrame", {
696-
configurable: true,
697-
value: previousRequestAnimationFrame,
698-
});
662+
try {
663+
let resolveRefresh!: () => void;
664+
refreshChatMock.mockReturnValueOnce(
665+
new Promise<void>((resolve) => {
666+
resolveRefresh = resolve;
667+
}),
668+
);
669+
const state = {
670+
chatManualRefreshInFlight: false,
671+
chatNewMessagesBelow: true,
672+
updateComplete: Promise.resolve(),
673+
resetToolStream: vi.fn(),
674+
scrollToBottom: vi.fn(),
675+
} as unknown as Parameters<typeof handleChatManualRefresh>[0];
676+
677+
const run = handleChatManualRefresh(state);
678+
await Promise.resolve();
679+
680+
expect(state.scrollToBottom).not.toHaveBeenCalled();
681+
resolveRefresh();
682+
await run;
683+
684+
expect(refreshChatMock).toHaveBeenCalledWith(state, {
685+
awaitHistory: true,
686+
scheduleScroll: false,
687+
});
688+
expect(state.scrollToBottom).toHaveBeenCalledWith({ smooth: true });
689+
expect(state.chatManualRefreshInFlight).toBe(true);
690+
expect(animationFrame.callback).toBeTypeOf("function");
691+
692+
const callback = animationFrame.callback;
693+
if (!callback) {
694+
throw new Error("expected manual refresh to schedule a frame callback");
695+
}
696+
callback(0);
697+
698+
expect(state.chatManualRefreshInFlight).toBe(false);
699+
expect(state.chatNewMessagesBelow).toBe(false);
700+
} finally {
701+
Object.defineProperty(globalThis, "requestAnimationFrame", {
702+
configurable: true,
703+
value: previousRequestAnimationFrame,
704+
});
705+
}
699706
});
700707
});
701708

0 commit comments

Comments
 (0)