Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

171 lines
4.9 KiB
TypeScript

import { act, renderHook } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import {
buildAgentComposerDraft,
emptyAgentComposerDraft
} from "../model/agentComposerDraft";
import type { AgentComposerInputHistoryEntry } from "../model/agentComposerInputHistory";
import { useComposerInputHistory } from "./useComposerInputHistory";
describe("useComposerInputHistory", () => {
it("supports repeated Up presses before the controlled draft rerenders", () => {
const onDraftContentChange = vi.fn();
const entries = historyEntries("one", "two");
const draftByScopeKeyRef = {
current: { "session:session-1": emptyAgentComposerDraft() }
};
const { result } = renderHook(() =>
useComposerInputHistory({
agentSessionId: "session-1",
currentDraft: emptyAgentComposerDraft(),
draftByScopeKeyRef,
draftScopeKey: "session:session-1",
entries,
hasOlderPage: false,
isLoadingOlderPage: false,
onDraftContentChange,
runtime: null,
workspaceId: "workspace-1"
})
);
act(() => {
expect(result.current.onHistoryNavigation("older")).toBe(true);
expect(result.current.onHistoryNavigation("older")).toBe(true);
});
expect(onDraftContentChange).toHaveBeenNthCalledWith(
1,
entries[1]!.draft,
"session:session-1"
);
expect(onDraftContentChange).toHaveBeenNthCalledWith(
2,
entries[0]!.draft,
"session:session-1"
);
});
it("recalls the next older entry after an older page is prepended", () => {
const onDraftContentChange = vi.fn();
const onRequestOlderPage = vi.fn();
const current = historyEntries("current")[0]!;
const older = historyEntries("older")[0]!;
const draftByScopeKeyRef = {
current: { "session:session-1": emptyAgentComposerDraft() }
};
const { result, rerender } = renderHook(
({ currentDraft, entries }) => {
draftByScopeKeyRef.current["session:session-1"] = currentDraft;
return useComposerInputHistory({
agentSessionId: "session-1",
currentDraft,
draftByScopeKeyRef,
draftScopeKey: "session:session-1",
entries,
hasOlderPage: true,
isLoadingOlderPage: false,
onDraftContentChange,
onRequestOlderPage,
runtime: null,
workspaceId: "workspace-1"
});
},
{
initialProps: {
currentDraft: emptyAgentComposerDraft(),
entries: [current]
}
}
);
act(() => {
result.current.onHistoryNavigation("older");
result.current.onHistoryNavigation("older");
});
expect(onRequestOlderPage).toHaveBeenCalledOnce();
rerender({
currentDraft: current.draft,
entries: [older, current]
});
act(() => {
result.current.settlePendingInputHistory();
});
expect(onDraftContentChange).toHaveBeenLastCalledWith(
older.draft,
"session:session-1"
);
});
it("does not overwrite edits made while an older page is loading", () => {
const onDraftContentChange = vi.fn();
const onRequestOlderPage = vi.fn();
const current = historyEntries("current")[0]!;
const older = historyEntries("older")[0]!;
const editedDraft = buildAgentComposerDraft({ prompt: "edited" });
const draftByScopeKeyRef = {
current: { "session:session-1": emptyAgentComposerDraft() }
};
const { result, rerender } = renderHook(
({ currentDraft, entries, isLoadingOlderPage }) => {
draftByScopeKeyRef.current["session:session-1"] = currentDraft;
return useComposerInputHistory({
agentSessionId: "session-1",
currentDraft,
draftByScopeKeyRef,
draftScopeKey: "session:session-1",
entries,
hasOlderPage: true,
isLoadingOlderPage,
onDraftContentChange,
onRequestOlderPage,
runtime: null,
workspaceId: "workspace-1"
});
},
{
initialProps: {
currentDraft: emptyAgentComposerDraft(),
entries: [current],
isLoadingOlderPage: false
}
}
);
act(() => {
result.current.onHistoryNavigation("older");
result.current.onHistoryNavigation("older");
});
rerender({
currentDraft: editedDraft,
entries: [current],
isLoadingOlderPage: true
});
rerender({
currentDraft: editedDraft,
entries: [older, current],
isLoadingOlderPage: false
});
act(() => {
result.current.settlePendingInputHistory();
});
expect(onDraftContentChange).toHaveBeenCalledOnce();
expect(onDraftContentChange).toHaveBeenLastCalledWith(
current.draft,
"session:session-1"
);
});
});
function historyEntries(
...prompts: string[]
): AgentComposerInputHistoryEntry[] {
return prompts.map((prompt) => ({
id: prompt,
draft: buildAgentComposerDraft({ prompt })
}));
}