返回 DeepSeek-Reasonix
composer-guidance-turn-race.test.tsx
根目录 / desktop / frontend / src / __tests__ / composer-guidance-turn-race.test.tsx
1 import assert from "node:assert/strict";
2 import { act } from "react";
3 import { installDom, installBridgeApp, renderComposer } from "./composerInboxHarness";
4
5 const dom = installDom();
6 const target = { tabId: "tab-a", sessionPath: "session-a", generation: 1, selection: 0, remote: false };
7 let resolveReceipt!: (value: { itemId: string; disposition: string; position: number; paused: boolean }) => void;
8 const receipt = new Promise<{ itemId: string; disposition: string; position: number; paused: boolean }>(resolve => { resolveReceipt = resolve; });
9 let submitted: unknown[] | undefined;
10 let saved = false;
11 installBridgeApp({
12 CaptureInboxTarget: async () => target,
13 ListTabs: async () => [{ id: "tab-a" }],
14 InboxSnapshot: async () => ({ sessionPath: "session-a", revision: saved ? 2 : 1, mutationsSupported: true,
15 items: saved ? [{ id: "saved-guidance", preview: "preserve my guidance", state: "queued", intent: "followup", source: "desktop" }] : [] }),
16 EnqueueInboxFollowupForTarget: async (...args: unknown[]) => { submitted = args; return receipt; },
17 InboxQueueForTarget: async () => { throw new Error("missing turn must queue as follow-up"); },
18 });
19 const { root, rerender } = await renderComposer({ running: true, localDurableGuidance: false,
20 onSteer: async () => { throw new Error("captured targets must use durable receipt tracking"); } });
21 await rerender({ insertRequest: { id: 9101, text: "preserve my guidance", mode: "replace" } });
22 const button = document.querySelector<HTMLButtonElement>(".composer__queue-steer");
23 assert.ok(button);
24 await act(async () => { button.click(); });
25 for (let i = 0; i < 50 && !submitted; i++) {
26 await act(async () => { await new Promise(resolve => setTimeout(resolve, 0)); });
27 }
28 assert.ok(submitted, "a missing turn must reach durable follow-up submission");
29 assert.deepEqual(submitted.slice(0, 4), [target, "preserve my guidance", "preserve my guidance", []]);
30 assert.match(String(submitted[4]), /^followup-/);
31 assert.equal(document.querySelector<HTMLTextAreaElement>("textarea.composer__input")?.value, "preserve my guidance", "draft must survive until durable receipt");
32 await act(async () => {
33 saved = true;
34 resolveReceipt({ itemId: "saved-guidance", disposition: "queued_followup", position: 1, paused: false });
35 await new Promise(resolve => setTimeout(resolve, 0));
36 });
37 const renderDeadline = Date.now() + 5000;
38 while (Date.now() < renderDeadline && !document.querySelector(".inbox-queue__list")) {
39 await act(async () => { await new Promise(resolve => setTimeout(resolve, 10)); });
40 }
41 assert.ok(document.querySelector(".inbox-queue__list")?.textContent?.includes("preserve my guidance"), `durable receipt should show queued guidance: ${document.body.textContent}`);
42 assert.equal(document.querySelector<HTMLTextAreaElement>("textarea.composer__input")?.value, "", "only the receipt clears the draft");
43 assert.equal(document.querySelector(".toast--warn"), null);
44 await act(async () => root.unmount());
45 dom.window.close();
46 console.log("composer turn-discovery race preserves guidance through durable receipt");
47
47 lines Plain Text