返回 DeepSeek-Reasonix
composer-queued-followup-outcome.test.tsx
根目录 / desktop / frontend / src / __tests__ / composer-queued-followup-outcome.test.tsx
1 // A busy-window submit (the desktop thinks idle, the serve is still finishing)
2 // is answered with a durable queue receipt. The composer must show the queue
3 // entry immediately and treat the message as delivered-to-queue, never as a
4 // failure — local busy sends behave the same way.
5 import assert from "node:assert/strict";
6 import { act } from "react";
7 import { installDom, installBridgeApp, renderComposer } from "./composerInboxHarness";
8
9 const flush = () => new Promise<void>(resolve => setTimeout(resolve, 0));
10
11 async function paste(text: string) {
12 await act(async () => {
13 const event = new window.Event("paste", { bubbles: true, cancelable: true });
14 Object.defineProperty(event, "clipboardData", { value: { files: [], items: [], types: ["text/plain"], getData: () => text } });
15 document.querySelector("textarea.composer__input:not([aria-hidden=true])")!.dispatchEvent(event);
16 await flush();
17 });
18 }
19 async function send() {
20 await act(async () => { (document.querySelector(".composer__btn--send") as HTMLButtonElement).click(); await flush(); });
21 }
22 const emptySnapshot = { revision: 1, paused: false, recovered: false, items: [], itemsCount: 0, bytes: 0, maxItems: 64, maxBytes: 1024 };
23 const queuedSnapshot = {
24 revision: 2, paused: false, recovered: false, itemsCount: 1, bytes: 12, maxItems: 64, maxBytes: 1024,
25 items: [{ id: "queued-1", intent: "followup", state: "queued", preview: "queued while busy" }],
26 };
27 const queuedError = () => Object.assign(new Error("reasonix_error:queued_followup"), {
28 code: "business",
29 data: { method: "SubmitRemoteTabWithSubmission", queuedFollowup: { itemId: "queued-1", disposition: "queued_followup", position: 0, paused: false } },
30 });
31
32 installDom();
33 installBridgeApp({
34 InboxQueueForTarget: async () => ({ outcome: "applied", snapshot: emptySnapshot }),
35 InboxSnapshot: async () => queuedSnapshot,
36 });
37 await renderComposer({
38 running: false,
39 inboxSessionPath: "session-a",
40 onSend: async () => { throw queuedError(); },
41 });
42 await paste("queued while busy");
43 await send();
44
45 // The queue strip reconciles through the snapshot refresh; let it settle.
46 await act(async () => { await flush(); await flush(); await new Promise(r => setTimeout(r, 60)); await flush(); });
47 const body = document.body.textContent ?? "";
48 assert.ok(body.includes("queued while busy"), "the queued message is visible in the pending queue");
49 assert.equal(body.includes("Send failed"), false, "a queued submit is not reported as a failed send");
50 assert.equal(body.includes("Refresh the session"), false, "a queued submit never asks the user to refresh");
51 assert.equal((document.querySelector("textarea.composer__input:not([aria-hidden=true])") as HTMLTextAreaElement).value, "",
52 "the composer clears once the message is queued");
53
54 console.log("composer queued follow-up outcome: busy submits surface as a visible queue entry");
55 process.exit(0);
56
56 lines Plain Text