mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(studio): renew each runtime application confirmation
This commit is contained in:
1 parent
f1bae78d39
commit
20c883c724
2 files changed
+101
-19
No files matched your search
@@ -21,52 +21,45 @@ export function useRuntimeReload(port: AgentPort, onDone: () => void) {
|
||||
const currentConnection = useRef(connection);
|
||||
currentConnection.current = connection;
|
||||
const attempts = useRef(0);
|
||||
const [state, setState] = useState<State>("");
|
||||
const [note, setNote] = useState("");
|
||||
const [feedback, setFeedback] = useState<{ state: State; note: string }>({ state: "", note: "" });
|
||||
const { state, note } = feedback;
|
||||
if (connection.port !== port) {
|
||||
setConnection({ port });
|
||||
setState("");
|
||||
setNote("");
|
||||
setFeedback({ state: "", note: "" });
|
||||
}
|
||||
|
||||
const go = useCallback(async () => {
|
||||
const attempt = ++attempts.current;
|
||||
setState("run");
|
||||
setNote(t("正在重启常驻进程,重新扫描技能、命令和钩子…"));
|
||||
setFeedback({ state: "run", note: t("正在重启常驻进程,重新扫描技能、命令和钩子…") });
|
||||
try {
|
||||
await port.reloadExtensions();
|
||||
if (currentConnection.current !== connection || attempts.current !== attempt) return;
|
||||
setState("ok");
|
||||
setNote(t("已生效,下一轮开始用新的扩展"));
|
||||
setFeedback({ state: "ok", note: t("已生效,下一轮开始用新的扩展") });
|
||||
onDone();
|
||||
} catch (e) {
|
||||
if (currentConnection.current !== connection || attempts.current !== attempt) return;
|
||||
// A refusal is the kernel's, and it knows why: a turn in flight, a
|
||||
// background job, a session that moved. Say its reason, not a generic one.
|
||||
setState("bad");
|
||||
setNote(reason(e));
|
||||
setFeedback({ state: "bad", note: reason(e) });
|
||||
}
|
||||
}, [port, onDone, connection]);
|
||||
|
||||
useEffect(() => {
|
||||
if (state !== "ok") return;
|
||||
if (feedback.state !== "ok") return;
|
||||
const id = setTimeout(() => {
|
||||
setState("");
|
||||
setNote("");
|
||||
setFeedback({ state: "", note: "" });
|
||||
}, SETTLED_MS);
|
||||
return () => clearTimeout(id);
|
||||
}, [state]);
|
||||
}, [feedback]);
|
||||
|
||||
return {
|
||||
applied: () => {
|
||||
attempts.current++;
|
||||
setState("ok");
|
||||
setNote(t("已生效,下一轮开始用新的扩展"));
|
||||
setFeedback({ state: "ok", note: t("已生效,下一轮开始用新的扩展") });
|
||||
},
|
||||
report: (message: string) => {
|
||||
attempts.current++;
|
||||
setState("bad");
|
||||
setNote(t("更改已保存,运行时未重载:{reason}。请用「重载运行时」重试。", { reason: message }));
|
||||
setFeedback({ state: "bad", note: t("更改已保存,运行时未重载:{reason}。请用「重载运行时」重试。", { reason: message }) });
|
||||
},
|
||||
action: (
|
||||
<button className="act reload" data-action="extensions.reload" data-s={state || undefined} disabled={state === "run"} onClick={go}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @vitest-environment jsdom
|
||||
import { afterEach, expect, it, vi } from "vitest";
|
||||
import { act, cleanup, render, screen, within } from "@testing-library/react";
|
||||
import { act, cleanup, fireEvent, render, screen, within } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import "./testkit";
|
||||
import { Settings } from "./Settings";
|
||||
@@ -12,6 +12,7 @@ import type { AgentPort, PluginPackage, SessionStatus } from "../port/port";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.useRealTimers();
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
localStorage.setItem(STORAGE, "zh");
|
||||
@@ -138,3 +139,91 @@ it("clears the earlier warning when a later saved toggle reloads successfully",
|
||||
expect(calls.mock.calls.filter(([url]) => url.endsWith("/plugins/enabled"))).toHaveLength(2);
|
||||
expect(calls.mock.calls.filter(([url]) => url.endsWith("/extensions/reload"))).toHaveLength(0);
|
||||
});
|
||||
|
||||
async function timedSettings() {
|
||||
const result = fixture(true);
|
||||
const changed = vi.fn();
|
||||
const view = render(draw(result.port, changed));
|
||||
await screen.findByText("notes-kit");
|
||||
vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout"] });
|
||||
const click = (element: HTMLElement) => act(async () => { fireEvent.click(element); });
|
||||
const toggle = () => click(within(row()).getByRole("switch"));
|
||||
const advance = (ms: number) => act(async () => { await vi.advanceTimersByTimeAsync(ms); });
|
||||
return { ...result, changed, view, click, toggle, advance };
|
||||
}
|
||||
|
||||
it.each(["package", "manual"])("gives a later package application its own confirmation window after %s success", async (first) => {
|
||||
const { click, toggle, advance, changed, calls } = await timedSettings();
|
||||
if (first === "manual") await click(screen.getByRole("button", { name: t("重载运行时") }));
|
||||
else await toggle();
|
||||
expect(screen.getByText(t("已生效,下一轮开始用新的扩展"))).toBeTruthy();
|
||||
await advance(3500);
|
||||
await toggle();
|
||||
expect(changed).toHaveBeenCalledTimes(2);
|
||||
await advance(500);
|
||||
expect(screen.getByText(t("已生效,下一轮开始用新的扩展"))).toBeTruthy();
|
||||
expect(screen.getByRole<HTMLButtonElement>("button", { name: t("已生效") }).disabled).toBe(false);
|
||||
await advance(3499);
|
||||
expect(screen.getByText(t("已生效,下一轮开始用新的扩展"))).toBeTruthy();
|
||||
await advance(1);
|
||||
expect(screen.queryByText(t("已生效,下一轮开始用新的扩展"))).toBeNull();
|
||||
expect(screen.getByRole<HTMLButtonElement>("button", { name: t("重载运行时") }).disabled).toBe(false);
|
||||
expect(calls.mock.calls.filter(([url]) => url.endsWith("/plugins/enabled"))).toHaveLength(first === "manual" ? 1 : 2);
|
||||
expect(calls.mock.calls.filter(([url]) => url.endsWith("/extensions/reload"))).toHaveLength(first === "manual" ? 1 : 0);
|
||||
});
|
||||
|
||||
it("expires a single confirmation without extending it on a Settings rerender", async () => {
|
||||
const { port, changed, view, toggle, advance } = await timedSettings();
|
||||
await toggle();
|
||||
await advance(3500);
|
||||
view.rerender(draw(port, changed));
|
||||
await advance(499);
|
||||
expect(screen.getByText(t("已生效,下一轮开始用新的扩展"))).toBeTruthy();
|
||||
await advance(1);
|
||||
expect(screen.queryByText(t("已生效,下一轮开始用新的扩展"))).toBeNull();
|
||||
expect(changed).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("retains a later saved-but-not-reloaded warning past the old success expiry", async () => {
|
||||
const { port, toggle, advance } = await timedSettings();
|
||||
await toggle();
|
||||
await advance(3500);
|
||||
vi.spyOn(port, "setPluginEnabled").mockResolvedValueOnce({ reloadError: refusal });
|
||||
await toggle();
|
||||
await advance(10000);
|
||||
expect(screen.getByText(t(warning, { reason: refusal }))).toBeTruthy();
|
||||
expect(screen.getByRole<HTMLButtonElement>("button", { name: t("重载运行时") }).disabled).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps a later manual reload pending past the old success expiry", async () => {
|
||||
const { port, click, toggle, advance, changed } = await timedSettings();
|
||||
await toggle();
|
||||
await advance(3500);
|
||||
let finish!: () => void;
|
||||
vi.spyOn(port, "reloadExtensions").mockImplementationOnce(() => new Promise<void>((resolve) => { finish = resolve; }));
|
||||
await click(screen.getByRole("button", { name: t("已生效") }));
|
||||
await advance(10000);
|
||||
expect(screen.getByRole<HTMLButtonElement>("button", { name: t("重载中") }).disabled).toBe(true);
|
||||
await act(async () => finish());
|
||||
await advance(3999);
|
||||
expect(screen.getByText(t("已生效,下一轮开始用新的扩展"))).toBeTruthy();
|
||||
await advance(1);
|
||||
expect(screen.queryByText(t("已生效,下一轮开始用新的扩展"))).toBeNull();
|
||||
expect(changed).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("does not let a previous connection's confirmation expire the new connection's success", async () => {
|
||||
const { changed, view, click, toggle, advance } = await timedSettings();
|
||||
await toggle();
|
||||
await advance(3500);
|
||||
const next = new MockPort() as unknown as AgentPort;
|
||||
vi.spyOn(next, "reloadExtensions").mockResolvedValue();
|
||||
await act(async () => view.rerender(draw(next, changed)));
|
||||
expect(screen.queryByText(t("已生效,下一轮开始用新的扩展"))).toBeNull();
|
||||
await click(screen.getByRole("button", { name: t("重载运行时") }));
|
||||
await advance(3999);
|
||||
expect(screen.getByText(t("已生效,下一轮开始用新的扩展"))).toBeTruthy();
|
||||
await advance(1);
|
||||
expect(screen.queryByText(t("已生效,下一轮开始用新的扩展"))).toBeNull();
|
||||
expect(changed).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
Reference in new issue
Block a user