Files
nyaterm/src/lib/childWindowCommandQueue.test.ts
T
Kang 325d07905c feat(child-window): enhance command queue with failure handling and recovery
- Updated `ChildWindowCommandQueue` to manage command states using a status system (`loading`, `ready`, `failed`) instead of a boolean flag.
- Implemented methods to mark commands as failed, drop queued commands, and recover from failed states when new tokens are registered.
- Added tests to verify the behavior of failure handling and recovery scenarios in the command queue.
- Enhanced `windowManager` to close failed child windows and clear their lifecycle appropriately.
2026-08-15 15:39:22 +08:00

120 lines
5.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { ChildWindowCommandQueue } from "./childWindowCommandQueue";
import { CHILD_WINDOW_COMMANDS } from "./childWindowProtocol";
describe("ChildWindowCommandQueue", () => {
it("keeps commands in FIFO order until the matching listener is ready", () => {
const queue = new ChildWindowCommandQueue();
queue.register("file-editor-main", "token-new", CHILD_WINDOW_COMMANDS.remoteFileEditorOpen);
expect(
queue.dispatch("file-editor-main", CHILD_WINDOW_COMMANDS.remoteFileEditorOpen, { name: "a" }),
).toEqual([]);
expect(
queue.dispatch("file-editor-main", CHILD_WINDOW_COMMANDS.remoteFileEditorOpen, { name: "b" }),
).toEqual([]);
expect(
queue.markReady("file-editor-main", "token-new", CHILD_WINDOW_COMMANDS.remoteFileEditorOpen),
).toEqual([
{ event: CHILD_WINDOW_COMMANDS.remoteFileEditorOpen, payload: { name: "a" } },
{ event: CHILD_WINDOW_COMMANDS.remoteFileEditorOpen, payload: { name: "b" } },
]);
});
it("ignores a ready event from a stale WebView token", () => {
const queue = new ChildWindowCommandQueue();
queue.register("settings", "token-new", CHILD_WINDOW_COMMANDS.settingsOpenTab);
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "appearance" });
expect(queue.markReady("settings", "token-old", CHILD_WINDOW_COMMANDS.settingsOpenTab)).toEqual(
[],
);
});
it("dispatches immediately after the listener is ready", () => {
const queue = new ChildWindowCommandQueue();
queue.register("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab);
queue.markReady("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab);
expect(
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "general" }),
).toEqual([{ event: CHILD_WINDOW_COMMANDS.settingsOpenTab, payload: { tab: "general" } }]);
});
it("queues new commands again while the child page reloads", () => {
const queue = new ChildWindowCommandQueue();
queue.register("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab);
queue.markReady("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab);
expect(queue.markLoading("settings", "token")).toBe(true);
expect(
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "appearance" }),
).toEqual([]);
expect(queue.markReady("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab)).toEqual([
{ event: CHILD_WINDOW_COMMANDS.settingsOpenTab, payload: { tab: "appearance" } },
]);
});
it("keeps direct dispatch compatibility for an untracked window", () => {
const queue = new ChildWindowCommandQueue();
expect(
queue.dispatch("legacy-window", CHILD_WINDOW_COMMANDS.filePreviewOpen, { name: "a.png" }),
).toEqual([{ event: CHILD_WINDOW_COMMANDS.filePreviewOpen, payload: { name: "a.png" } }]);
});
it("clears state when a window is destroyed", () => {
const queue = new ChildWindowCommandQueue();
queue.register("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab);
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "appearance" });
queue.clear("settings");
expect(
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "general" }),
).toEqual([{ event: CHILD_WINDOW_COMMANDS.settingsOpenTab, payload: { tab: "general" } }]);
});
it("marks a matching token as failed and drops queued commands", () => {
const queue = new ChildWindowCommandQueue();
queue.register("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab);
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "appearance" });
expect(queue.markFailed("settings", "token")).toBe(true);
expect(queue.isFailed("settings", "token")).toBe(true);
expect(
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "general" }),
).toEqual([]);
expect(queue.markReady("settings", "token", CHILD_WINDOW_COMMANDS.settingsOpenTab)).toEqual([]);
});
it("recovers from a failed state when a new token is registered", () => {
const queue = new ChildWindowCommandQueue();
queue.register("settings", "token-old", CHILD_WINDOW_COMMANDS.settingsOpenTab);
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "appearance" });
queue.markFailed("settings", "token-old");
queue.register("settings", "token-new", CHILD_WINDOW_COMMANDS.settingsOpenTab);
expect(queue.isFailed("settings", "token-new")).toBe(false);
expect(
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "general" }),
).toEqual([]);
expect(queue.markReady("settings", "token-new", CHILD_WINDOW_COMMANDS.settingsOpenTab)).toEqual([
{ event: CHILD_WINDOW_COMMANDS.settingsOpenTab, payload: { tab: "general" } },
]);
});
it("ignores a failed event from a stale WebView token", () => {
const queue = new ChildWindowCommandQueue();
queue.register("settings", "token-new", CHILD_WINDOW_COMMANDS.settingsOpenTab);
queue.dispatch("settings", CHILD_WINDOW_COMMANDS.settingsOpenTab, { tab: "appearance" });
expect(queue.markFailed("settings", "token-old")).toBe(false);
expect(queue.isFailed("settings")).toBe(false);
expect(queue.markReady("settings", "token-new", CHILD_WINDOW_COMMANDS.settingsOpenTab)).toEqual([
{ event: CHILD_WINDOW_COMMANDS.settingsOpenTab, payload: { tab: "appearance" } },
]);
});
});