Closes #252: port vanilla-DOM tests to React Testing Library

Rewrites the 27 DOM-behavior test files (dialogs, sidebar, spawn form,
command bar, structured pane, catalog, multiview, theme, etc.) to use
screen/within/fireEvent/waitFor instead of manual querySelector and
dispatchEvent. Pure-logic test files (matcher, tree, activity, paths,
reconnect, keys, webgl, paneTitle, commands, daemonLink, events, layout,
persistence, previewSchedule, projectList, spawnSimilar, structuredDiff,
toolIcon, fastForward, spawnForm) are untouched, and preview.test.ts
needed no changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-09-09 02:06:31 +01:00
co-authored by Claude Sonnet 5
parent 8304ef9a49
commit c9562ab5eb
27 changed files with 894 additions and 846 deletions
+35 -36
View File
@@ -1,4 +1,6 @@
// @vitest-environment happy-dom
import "@testing-library/jest-dom/vitest";
import { fireEvent, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
@@ -45,23 +47,29 @@ afterEach(() => {
document.body.replaceChildren();
});
function input(): HTMLInputElement {
return screen.getByRole("combobox") as HTMLInputElement;
}
function press(key: string, init: KeyboardEventInit = {}): void {
const input = document.querySelector(".cmdbar-input") as HTMLInputElement;
input.dispatchEvent(new KeyboardEvent("keydown", { key, bubbles: true, ...init }));
fireEvent.keyDown(input(), { key, ...init });
}
function type(value: string): void {
const input = document.querySelector(".cmdbar-input") as HTMLInputElement;
input.value = value;
input.dispatchEvent(new Event("input", { bubbles: true }));
// The bar re-renders off a native "input" listener, not React's "change" —
// fireEvent.input is the one that actually reaches it.
fireEvent.input(input(), { target: { value } });
}
function rowNames(): string[] {
return [...document.querySelectorAll(".cmdbar-row-name")].map((r) => r.textContent ?? "");
return screen
.queryAllByRole("option")
.map((row) => row.querySelector(".cmdbar-row-name")?.textContent ?? "");
}
function activeName(): string | undefined {
return document.querySelector(".cmdbar-row.is-active .cmdbar-row-name")?.textContent ?? undefined;
const active = screen.queryByRole("option", { selected: true });
return active?.querySelector(".cmdbar-row-name")?.textContent ?? undefined;
}
describe("empty-query ordering", () => {
@@ -211,7 +219,7 @@ describe("the overlay", () => {
it("opening twice leaves one overlay", () => {
open();
open();
expect(document.querySelectorAll(".cmdbar-overlay")).toHaveLength(1);
expect(screen.getAllByRole("dialog")).toHaveLength(1);
expect(isCommandBarOpen()).toBe(true);
});
@@ -226,8 +234,7 @@ describe("the overlay", () => {
it("highlights inside the directory when that is the field that matched", () => {
open();
type("notes");
const row = document.querySelector(".cmdbar-row") as HTMLElement;
expect(row.dataset.sessionId).toBe("notes");
expect(screen.getByRole("option")).toHaveAttribute("data-session-id", "notes");
type("dev/wor");
expect(rowNames()).toEqual(["worker"]);
expect(
@@ -239,7 +246,7 @@ describe("the overlay", () => {
open();
type("zzz");
expect(rowNames()).toEqual([]);
expect(document.querySelector(".cmdbar-empty")?.textContent).toBe("No matches");
expect(screen.getByText("No matches")).toBeDefined();
});
it("moves the highlight with the arrows and ctrl-p/ctrl-n, without wrapping", () => {
@@ -261,7 +268,7 @@ describe("the overlay", () => {
press("ArrowDown");
expect(activeName()).toBe("worker");
type("o");
expect(activeName()).toBe(document.querySelector(".cmdbar-row-name")?.textContent);
expect(activeName()).toBe(rowNames()[0]);
});
it("opens the highlighted session through the shared open path on Enter", () => {
@@ -291,17 +298,13 @@ describe("the overlay", () => {
expect(isCommandBarOpen()).toBe(false);
open();
(document.querySelector(".cmdbar-overlay") as HTMLElement).dispatchEvent(
new MouseEvent("mousedown", { bubbles: true }),
);
fireEvent.mouseDown(screen.getByRole("dialog"));
expect(isCommandBarOpen()).toBe(false);
});
it("stays open for a press inside the panel", () => {
open();
(document.querySelector(".cmdbar-panel") as HTMLElement).dispatchEvent(
new MouseEvent("mousedown", { bubbles: true }),
);
fireEvent.mouseDown(document.querySelector(".cmdbar-panel") as HTMLElement);
expect(isCommandBarOpen()).toBe(true);
});
@@ -310,7 +313,7 @@ describe("the overlay", () => {
document.body.appendChild(before);
before.focus();
open();
expect(document.activeElement).toBe(document.querySelector(".cmdbar-input"));
expect(screen.getByRole("combobox")).toHaveFocus();
press("Escape");
expect(document.activeElement).toBe(before);
});
@@ -318,7 +321,7 @@ describe("the overlay", () => {
it("clicking a row opens that session", () => {
const onOpenSession = vi.fn();
open({ sessions, homeDir: "/home/me", commands: [], onOpenSession });
(document.querySelectorAll(".cmdbar-row")[1] as HTMLElement).click();
fireEvent.click(screen.getAllByRole("option")[1]);
expect(onOpenSession).toHaveBeenCalledWith("worker");
expect(isCommandBarOpen()).toBe(false);
});
@@ -330,12 +333,10 @@ function command(id: string, label: string, keys: string[], run = () => {}): Com
return { id, label, group: "Test", keys, scope: "global", run };
}
const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
function rowKeys(): string[][] {
return [...document.querySelectorAll(".cmdbar-row-keys")].map((el) =>
[...el.querySelectorAll("kbd")].map((k) => k.textContent ?? ""),
);
return screen
.queryAllByRole("option")
.map((row) => [...row.querySelectorAll("kbd")].map((k) => k.textContent ?? ""));
}
describe("parsing the query line", () => {
@@ -460,8 +461,7 @@ describe("the sigils in the overlay", () => {
it("lists recent dirs then projects under `~`", async () => {
open({ loadDirectories: async () => ["/home/me/dev/api", "/srv/www"] });
type("~");
await flush();
expect(rowNames()).toEqual(["~/dev/api", "/srv/www"]);
await waitFor(() => expect(rowNames()).toEqual(["~/dev/api", "/srv/www"]));
expect(document.querySelectorAll(".cmdbar-group-title")).toHaveLength(0);
});
@@ -476,9 +476,8 @@ describe("the sigils in the overlay", () => {
});
// The second `~` is a literal query character and reaches the matcher.
type("~~/dev/mae");
await flush();
await waitFor(() => expect(rowNames()).toEqual(["~/dev/maestro"]));
expect(asked).toEqual(["~/dev/"]);
expect(rowNames()).toEqual(["~/dev/maestro"]);
});
it("spawns in the highlighted directory on Enter", async () => {
@@ -488,7 +487,7 @@ describe("the sigils in the overlay", () => {
onSpawnDirectory: (path) => spawned.push(path),
});
type("~api");
await flush();
await waitFor(() => expect(rowNames()).toEqual(["~/dev/api"]));
press("Enter");
expect(spawned).toEqual(["/home/me/dev/api"]);
expect(isCommandBarOpen()).toBe(false);
@@ -507,7 +506,7 @@ describe("the sigils in the overlay", () => {
listDirEntries: async () => [],
});
type("~");
await flush();
await waitFor(() => expect(rowNames()).toEqual(["~/dev/api"]));
press("Enter");
document.removeEventListener(SPAWN_DIRECTORY_EVENT, listener);
expect(seen).toEqual(["/home/me/dev/api"]);
@@ -526,7 +525,7 @@ describe("the focus round-trip over a pane (#152)", () => {
const ta = terminalTextarea();
ta.focus();
openCommandBar({ sessions: [], commands: [] });
expect(document.activeElement).toBe(document.querySelector(".cmdbar-input"));
expect(screen.getByRole("combobox")).toHaveFocus();
press("Escape");
expect(document.activeElement).toBe(ta);
});
@@ -605,7 +604,7 @@ describe("the session action page (#151)", () => {
type("spwn");
expect(rowNames()).toEqual(["Spawn similar"]);
type("zzz");
expect(document.querySelector(".cmdbar-empty")?.textContent).toBe("No matches");
expect(screen.getByText("No matches")).toBeDefined();
});
it("does nothing on a command row", () => {
@@ -625,7 +624,7 @@ describe("the session action page (#151)", () => {
it("does nothing on a directory row", async () => {
open({ loadDirectories: () => Promise.resolve(["/home/me/dev/api"]) });
type("~");
await Promise.resolve();
await waitFor(() => expect(rowNames()).toEqual(["~/dev/api"]));
press("Tab");
expect(rowNames()).toEqual(["~/dev/api"]);
expect(headerText()).toBeUndefined();
@@ -639,7 +638,7 @@ describe("the session action page (#151)", () => {
press("Escape");
expect(isCommandBarOpen()).toBe(true);
expect(headerText()).toBeUndefined();
expect((document.querySelector(".cmdbar-input") as HTMLInputElement).value).toBe("wor");
expect(input().value).toBe("wor");
expect(rowNames()).toEqual(["worker"]);
});
@@ -650,7 +649,7 @@ describe("the session action page (#151)", () => {
type("");
press("Backspace");
expect(headerText()).toBeUndefined();
expect((document.querySelector(".cmdbar-input") as HTMLInputElement).value).toBe("wor");
expect(input().value).toBe("wor");
expect(rowNames()).toEqual(["worker"]);
});