返回 DeepSeek-Reasonix
composer-model-target.test.tsx
根目录 / desktop / frontend / src / __tests__ / composer-model-target.test.tsx
1 import assert from "node:assert/strict";
2 import { act } from "react";
3 import { installDom, installBridgeApp, renderComposer } from "./composerInboxHarness";
4 import { emptySessionInput } from "../lib/sessionComposerPersistence";
5 import { makeSessionUIMock } from "../lib/sessionUIMock";
6
7 const dom = installDom();
8 globalThis.HTMLInputElement = dom.window.HTMLInputElement;
9 const calls: string[] = [];
10 const picked: string[] = [];
11 const catalog = [
12 { ref: "configured/flash", provider: "configured", model: "flash", current: true },
13 { ref: "configured/pro", provider: "configured", model: "pro", current: false },
14 ];
15 installBridgeApp({
16 ...makeSessionUIMock(async () => {}),
17 ModelsForTab: async (id: string) => { calls.push(`tab:${id}`); return catalog; },
18 ModelsForDraft: async (id: string) => {
19 calls.push(`draft:${id}`);
20 return id === "legacy-draft" ? catalog : [];
21 },
22 });
23 async function waitFor(check: () => boolean) {
24 for (let i = 0; i < 100 && !check(); i++) {
25 await act(async () => { await new Promise(resolve => setTimeout(resolve, 10)); });
26 }
27 assert.ok(check(), "model picker should become ready");
28 }
29 const { root, rerender } = await renderComposer({
30 tabId: "formal-tab", sessionKey: "formal-session", modelLabel: "flash",
31 formalSessionRef: { hostId: "local", sessionId: "formal-session" },
32 composerTarget: { kind: "session", tabId: "formal-tab" },
33 onSwitchModel: async ref => { picked.push(ref); return true; },
34 });
35 try {
36 await waitFor(() => calls.length > 0);
37 assert.ok(calls.every(call => call === "tab:formal-tab"), `formal input persistence must not select the draft catalog: ${calls}`);
38 await waitFor(() => !!document.querySelector<HTMLButtonElement>(".modelsw__trigger:not(:disabled)"));
39 await act(async () => { document.querySelector<HTMLButtonElement>(".modelsw__trigger")!.click(); });
40 await waitFor(() => document.querySelectorAll("[role=option]").length === 2);
41 await act(async () => { document.querySelector<HTMLButtonElement>("[role=option][aria-selected=false]")!.click(); });
42 assert.deepEqual(picked, ["configured/pro"]);
43
44 calls.length = 0;
45 await rerender({ tabId: "other-tab", sessionKey: "other-session",
46 formalSessionRef: { hostId: "local", sessionId: "other-session" },
47 composerTarget: { kind: "session", tabId: "other-tab" } });
48 await waitFor(() => calls.length > 0);
49 assert.ok(calls.every(call => call === "tab:other-tab"), "navigation must query the new formal tab");
50
51 calls.length = 0;
52 await rerender({ tabId: undefined, formalSessionRef: undefined, sessionKey: "legacy-draft",
53 composerTarget: { kind: "draft", draftId: "legacy-draft", generation: 1 },
54 persistentDraft: { draftId: "legacy-draft", generation: 1, revision: 1, initial: emptySessionInput(), onChange() {} } });
55 await waitFor(() => calls.length > 0);
56 assert.ok(calls.every(call => call === "draft:legacy-draft"), "recovered legacy drafts retain their catalog route");
57 await act(async () => { document.querySelector<HTMLButtonElement>(".modelsw__trigger")!.click(); });
58 await waitFor(() => document.querySelectorAll("[role=option]").length === 2);
59 console.log("PASS composer model catalog: formal persisted input, switching, navigation and recovered legacy draft");
60 } finally {
61 await act(async () => root.unmount());
62 dom.window.close();
63 }
64
64 lines Plain Text