Deletes the vanilla shell and mounts App.tsx on #root. main.ts and the 23 views it drove are gone; App.tsx composes the ported components — sidebar, multi-view, command bar, spawn modal, help and restart overlays, daemon link, drawer — around the catalog and layout stores. The pure parts of four deleted modules moved next to the components that still use them: the spawn-form model and spawnSimilar into components/spawn, the transcript's markdown/format helpers and the diff into components/structured, the help overlay's grouping into components/dialogs. Document-level event names now live in uiEvents.ts. Deduplicated two pairs the migration left side by side: the sidebar uses dialogs/KillDialog and common/ContextMenu. It also listens for the command bar's kill/rename events, which nothing had picked up since catalog.ts went away. styles.css stays as the token and base layer, with `#app` renamed to `#root` and the pane-body rules re-keyed off the classes the React panes actually carry. Closes #253
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
// @vitest-environment happy-dom
|
|
import { act, cleanup, render } from "@testing-library/react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { OPEN_COMMAND_BAR_EVENT } from "../../uiEvents.js";
|
|
import { useCommandBar, type UseCommandBarOptions } from "./useCommandBar.js";
|
|
|
|
let controls: ReturnType<typeof useCommandBar>;
|
|
|
|
function Probe(opts: UseCommandBarOptions) {
|
|
controls = useCommandBar(opts);
|
|
return <div>{controls.open ? "open" : "closed"}</div>;
|
|
}
|
|
|
|
function ctrlK(target: EventTarget = window): void {
|
|
act(() => {
|
|
target.dispatchEvent(
|
|
new KeyboardEvent("keydown", { key: "k", ctrlKey: true, bubbles: true, cancelable: true }),
|
|
);
|
|
});
|
|
}
|
|
|
|
afterEach(cleanup);
|
|
|
|
describe("useCommandBar", () => {
|
|
it("opens on ctrl-k, from the capture phase so an attached xterm never sees it", () => {
|
|
const view = render(<Probe />);
|
|
ctrlK();
|
|
expect(view.container.textContent).toBe("open");
|
|
});
|
|
|
|
it("stays deaf while the bar is already up — its own input owns the keys", () => {
|
|
const onOpen = vi.fn();
|
|
render(<Probe onOpen={onOpen} />);
|
|
ctrlK();
|
|
ctrlK();
|
|
expect(onOpen).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("leaves a form field's own Ctrl-K alone", () => {
|
|
const field = document.createElement("input");
|
|
document.body.appendChild(field);
|
|
const view = render(<Probe />);
|
|
ctrlK(field);
|
|
expect(view.container.textContent).toBe("closed");
|
|
field.remove();
|
|
});
|
|
|
|
it("opens from the sidebar's search field, the touch entry point", () => {
|
|
const view = render(<Probe />);
|
|
act(() => {
|
|
document.dispatchEvent(new CustomEvent(OPEN_COMMAND_BAR_EVENT));
|
|
});
|
|
expect(view.container.textContent).toBe("open");
|
|
});
|
|
|
|
it("runs the caller's hook before opening, so a preview is discarded first", () => {
|
|
const onOpen = vi.fn();
|
|
render(<Probe onOpen={onOpen} />);
|
|
ctrlK();
|
|
expect(onOpen).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("closes on demand", () => {
|
|
const view = render(<Probe />);
|
|
ctrlK();
|
|
act(() => controls.closeBar());
|
|
expect(view.container.textContent).toBe("closed");
|
|
});
|
|
|
|
it("binds nothing while disabled — another overlay owns the keyboard", () => {
|
|
const view = render(<Probe enabled={false} />);
|
|
ctrlK();
|
|
expect(view.container.textContent).toBe("closed");
|
|
});
|
|
});
|