




























@@ -35,6 +35,14 @@ function createHost() {
3535};
3636}
373738+type ComposerPersistHost = ReturnType<typeof createHost> & {
39+chatComposerPersistTimer?: ReturnType<typeof globalThis.setTimeout> | number | null;
40+};
41+42+function createComposerPersistHost(): ComposerPersistHost {
43+return createHost() as ComposerPersistHost;
44+}
45+3846describe("handleDisconnected", () => {
3947afterEach(() => {
4048vi.useRealTimers();
@@ -82,9 +90,143 @@ describe("handleDisconnected", () => {
82908391describe("handleUpdated", () => {
8492afterEach(() => {
93+vi.useRealTimers();
8594vi.unstubAllGlobals();
8695});
879697+it("debounces draft-only composer persistence", () => {
98+vi.useFakeTimers();
99+vi.stubGlobal("sessionStorage", createStorageMock());
100+const host = createHost();
101+host.chatMessage = "typing without blocking input";
102+103+handleUpdated(
104+host as unknown as Parameters<typeof handleUpdated>[0],
105+new Map<PropertyKey, unknown>([["chatMessage", ""]]),
106+);
107+108+expect(loadChatComposerSnapshot(host, "main")).toBeNull();
109+110+vi.advanceTimersByTime(199);
111+expect(loadChatComposerSnapshot(host, "main")).toBeNull();
112+113+vi.advanceTimersByTime(1);
114+expect(loadChatComposerSnapshot(host, "main")).toEqual({
115+draft: "typing without blocking input",
116+queue: [],
117+});
118+});
119+120+it("flushes delayed draft persistence on teardown", () => {
121+vi.useFakeTimers();
122+vi.stubGlobal("sessionStorage", createStorageMock());
123+vi.stubGlobal("window", {
124+removeEventListener: vi.fn(),
125+});
126+const host = createHost();
127+host.chatMessage = "save before close";
128+129+handleUpdated(
130+host as unknown as Parameters<typeof handleUpdated>[0],
131+new Map<PropertyKey, unknown>([["chatMessage", ""]]),
132+);
133+handleDisconnected(host as unknown as Parameters<typeof handleDisconnected>[0]);
134+135+expect(loadChatComposerSnapshot(host, "main")).toEqual({
136+draft: "save before close",
137+queue: [],
138+});
139+vi.advanceTimersByTime(200);
140+expect(loadChatComposerSnapshot(host, "main")).toEqual({
141+draft: "save before close",
142+queue: [],
143+});
144+});
145+146+it("persists queue changes immediately and clears delayed draft persistence", () => {
147+vi.useFakeTimers();
148+vi.stubGlobal("sessionStorage", createStorageMock());
149+const host = createComposerPersistHost();
150+host.chatMessage = "draft with queued work";
151+152+handleUpdated(
153+host as unknown as Parameters<typeof handleUpdated>[0],
154+new Map<PropertyKey, unknown>([["chatMessage", ""]]),
155+);
156+host.chatQueue = [{ id: "queued-1", text: "next prompt", createdAt: 1 }];
157+handleUpdated(
158+host as unknown as Parameters<typeof handleUpdated>[0],
159+new Map<PropertyKey, unknown>([["chatQueue", []]]),
160+);
161+162+expect(host.chatComposerPersistTimer).toBeNull();
163+expect(loadChatComposerSnapshot(host, "main")).toEqual({
164+draft: "draft with queued work",
165+queue: [{ id: "queued-1", text: "next prompt", createdAt: 1 }],
166+});
167+});
168+169+it("persists drafts immediately when the active session changes", () => {
170+vi.useFakeTimers();
171+vi.stubGlobal("sessionStorage", createStorageMock());
172+const host = createComposerPersistHost();
173+host.chatMessage = "draft before new chat";
174+175+handleUpdated(
176+host as unknown as Parameters<typeof handleUpdated>[0],
177+new Map<PropertyKey, unknown>([["chatMessage", ""]]),
178+);
179+host.sessionKey = "agent:main:new-session";
180+host.chatMessage = "draft restored into new chat";
181+handleUpdated(
182+host as unknown as Parameters<typeof handleUpdated>[0],
183+new Map<PropertyKey, unknown>([
184+["sessionKey", "main"],
185+["chatMessage", ""],
186+]),
187+);
188+189+expect(host.chatComposerPersistTimer).toBeNull();
190+expect(loadChatComposerSnapshot(host, "main")).toEqual({
191+draft: "draft before new chat",
192+queue: [],
193+});
194+expect(loadChatComposerSnapshot(host, "agent:main:new-session")).toEqual({
195+draft: "draft restored into new chat",
196+queue: [],
197+});
198+});
199+200+it("flushes delayed draft persistence when only the session changes", () => {
201+vi.useFakeTimers();
202+vi.stubGlobal("sessionStorage", createStorageMock());
203+const host = createComposerPersistHost();
204+host.chatMessage = "draft from old session";
205+206+handleUpdated(
207+host as unknown as Parameters<typeof handleUpdated>[0],
208+new Map<PropertyKey, unknown>([["chatMessage", ""]]),
209+);
210+host.sessionKey = "agent:main:other";
211+handleUpdated(
212+host as unknown as Parameters<typeof handleUpdated>[0],
213+new Map<PropertyKey, unknown>([["sessionKey", "main"]]),
214+);
215+216+expect(host.chatComposerPersistTimer).toBeNull();
217+expect(loadChatComposerSnapshot(host, "main")).toEqual({
218+draft: "draft from old session",
219+queue: [],
220+});
221+expect(loadChatComposerSnapshot(host, "agent:main:other")).toBeNull();
222+vi.advanceTimersByTime(200);
223+expect(loadChatComposerSnapshot(host, "agent:main:other")).toBeNull();
224+expect(loadChatComposerSnapshot(host, "main")).toEqual({
225+draft: "draft from old session",
226+queue: [],
227+});
228+});
229+88230it("persists chat draft and queue changes before chat refresh short-circuits", () => {
89231vi.stubGlobal("sessionStorage", createStorageMock());
90232const host = createHost();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。