feat(maestro): rename a session from the web sidebar
Right-clicking a card opens a custom context menu (rename, spawn similar, kill). Rename swaps the name for an inline field; Enter commits, Escape and blur cancel. The tree stops rebuilding while the field is open, so a catalog delta cannot replace what is being typed into. Refs #125 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bdeec6eff5
commit
43b90ccf0a
@@ -0,0 +1,58 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { closeContextMenu, openContextMenu } from "./contextMenu.js";
|
||||
|
||||
afterEach(() => {
|
||||
closeContextMenu();
|
||||
document.body.replaceChildren();
|
||||
});
|
||||
|
||||
function labels(): string[] {
|
||||
return [...document.querySelectorAll(".context-menu-item")].map((b) => b.textContent ?? "");
|
||||
}
|
||||
|
||||
describe("context menu (#125)", () => {
|
||||
it("renders one item per entry, in order", () => {
|
||||
openContextMenu(10, 20, [
|
||||
{ label: "Rename…", onSelect: () => {} },
|
||||
{ label: "Kill session", onSelect: () => {}, danger: true },
|
||||
]);
|
||||
expect(labels()).toEqual(["Rename…", "Kill session"]);
|
||||
expect(document.querySelector(".context-menu-item.is-danger")?.textContent).toBe(
|
||||
"Kill session",
|
||||
);
|
||||
});
|
||||
|
||||
it("runs the selected entry and closes", () => {
|
||||
const chosen = vi.fn();
|
||||
openContextMenu(0, 0, [{ label: "Rename…", onSelect: chosen }]);
|
||||
(document.querySelector(".context-menu-item") as HTMLButtonElement).click();
|
||||
expect(chosen).toHaveBeenCalledOnce();
|
||||
expect(document.querySelector(".context-menu")).toBeNull();
|
||||
});
|
||||
|
||||
it("opening a second menu replaces the first", () => {
|
||||
openContextMenu(0, 0, [{ label: "first", onSelect: () => {} }]);
|
||||
openContextMenu(0, 0, [{ label: "second", onSelect: () => {} }]);
|
||||
expect(document.querySelectorAll(".context-menu")).toHaveLength(1);
|
||||
expect(labels()).toEqual(["second"]);
|
||||
});
|
||||
|
||||
it("closes on Escape and on a press outside it", () => {
|
||||
openContextMenu(0, 0, [{ label: "Rename…", onSelect: () => {} }]);
|
||||
window.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
|
||||
expect(document.querySelector(".context-menu")).toBeNull();
|
||||
|
||||
openContextMenu(0, 0, [{ label: "Rename…", onSelect: () => {} }]);
|
||||
document.body.dispatchEvent(new Event("pointerdown", { bubbles: true }));
|
||||
expect(document.querySelector(".context-menu")).toBeNull();
|
||||
});
|
||||
|
||||
it("stays open for a press inside itself", () => {
|
||||
openContextMenu(0, 0, [{ label: "Rename…", onSelect: () => {} }]);
|
||||
const menu = document.querySelector(".context-menu") as HTMLElement;
|
||||
menu.dispatchEvent(new Event("pointerdown", { bubbles: true }));
|
||||
expect(document.querySelector(".context-menu")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user