返回 DeepSeek-Reasonix
context-panel-runtime.test.tsx
根目录 / desktop / frontend / src / __tests__ / context-panel-runtime.test.tsx
1 import assert from "node:assert/strict";
2 import { mock } from "node:test";
3 import { JSDOM } from "jsdom";
4 import React, { act } from "react";
5 import { createRoot } from "react-dom/client";
6 import { ContextPanel } from "../components/ContextPanel";
7 import { LocaleProvider } from "../lib/i18n";
8 import { installDesktopHostStub } from "./desktopHostStub";
9 import type { ContextPanelInfo } from "../lib/types";
10
11 const dom = new JSDOM('<html><body><div id="root"></div></body></html>', { url: "http://localhost", pretendToBeVisual: true });
12 Object.assign(globalThis, { window: dom.window, document: dom.window.document, Node: dom.window.Node,
13 HTMLElement: dom.window.HTMLElement, Event: dom.window.Event, IS_REACT_ACT_ENVIRONMENT: true,
14 ResizeObserver: class { observe() {} unobserve() {} disconnect() {} } });
15 Object.defineProperty(globalThis, "navigator", { value: { language: "en-US" }, configurable: true });
16 const start = 1_000_000;
17 mock.timers.enable({ apis: ["Date", "setTimeout", "setInterval"], now: start });
18
19 let info: ContextPanelInfo = {
20 usedTokens: 100, windowTokens: 1000, promptTokens: 100, completionTokens: 10,
21 totalTokens: 1000, reasoningTokens: 0, cacheHitTokens: 0, cacheMissTokens: 100,
22 sessionCacheHitTokens: 0, sessionCacheMissTokens: 100, sessionCompletionTokens: 10,
23 requestCount: 1, elapsedMs: 10_000, activeTurnStartedAt: start - 5_000, sessionCost: 1,
24 readFiles: [], changedFiles: [],
25 };
26 let calls = 0;
27 installDesktopHostStub({ ContextPanel: () => { calls++; return Promise.resolve(info); } });
28 const root = createRoot(document.getElementById("root")!);
29 let refreshKey = 0;
30 async function render() {
31 await act(async () => {
32 root.render(<LocaleProvider><ContextPanel tabId="tab-a" sessionGen={1} refreshKey={refreshKey}
33 context={{ used: 100, window: 1000, sessionTokens: 1000 }} /></LocaleProvider>);
34 });
35 }
36 async function tick(ms: number) { await act(async () => { mock.timers.tick(ms); }); }
37 const runtime = () => document.querySelectorAll(".context-panel__summary-rows .context-panel__mini-stat strong")[2]?.textContent;
38
39 try {
40 await render();
41 assert.equal(runtime(), "15s", "completed turns plus the running turn so far");
42 const fetched = calls;
43 // Tool execution: no usage event arrives, so no snapshot is refetched.
44 await tick(60_000);
45 assert.equal(calls, fetched, "the runtime advances without polling the host");
46 assert.equal(runtime(), "1m 15s", "a running turn keeps counting between model requests");
47
48 info = { ...info, elapsedMs: 80_000, activeTurnStartedAt: undefined };
49 refreshKey++;
50 await render();
51 await tick(30_000);
52 assert.equal(runtime(), "1m 20s", "an idle session holds its accumulated runtime");
53 console.log("context panel runtime: running turn ticks between requests, idle session holds");
54 } finally {
55 await act(async () => root.unmount());
56 mock.timers.reset();
57 dom.window.close();
58 }
59
59 lines Plain Text