/**
* #69 lot 3 — the mobile key toolbar.
*
* Guards the two properties that make the terminal drivable from a phone:
* the chips emit the exact byte sequences a physical key would, and tapping one
* never steals focus from xterm (which would collapse the virtual keyboard).
*/
import { describe, it, expect } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";
import type { TerminalInputApi } from "@/features/terminals";
import { TerminalKeyBar } from "./TerminalKeyBar";
function mockApi(): TerminalInputApi & { sent: string[]; focused: () => number } {
const sent: string[] = [];
let focusCount = 0;
return {
sent,
focused: () => focusCount,
send: (d) => void sent.push(d),
focus: () => void focusCount++,
};
}
describe("TerminalKeyBar", () => {
it("sends the byte sequence a physical key would emit", () => {
const api = mockApi();
render();
fireEvent.click(screen.getByRole("button", { name: "Ctrl-C (interrompre)" }));
fireEvent.click(screen.getByRole("button", { name: "Esc" }));
fireEvent.click(screen.getByRole("button", { name: "Tab" }));
fireEvent.click(screen.getByRole("button", { name: "Flèche haut" }));
expect(api.sent).toEqual(["\x03", "\x1b", "\t", "\x1b[A"]);
});
it("emits the CSI sequences for every arrow key", () => {
const api = mockApi();
render();
for (const name of ["Flèche haut", "Flèche bas", "Flèche gauche", "Flèche droite"]) {
fireEvent.click(screen.getByRole("button", { name }));
}
expect(api.sent).toEqual(["\x1b[A", "\x1b[B", "\x1b[D", "\x1b[C"]);
});
it("keeps the virtual keyboard up: cancels the tap's focus shift, refocuses xterm", () => {
const api = mockApi();
render();
const ctrlC = screen.getByRole("button", { name: "Ctrl-C (interrompre)" });
// `fireEvent` returns false when the handler called preventDefault — that is
// what stops the browser blurring xterm's textarea and closing the keyboard.
expect(fireEvent.pointerDown(ctrlC)).toBe(false);
fireEvent.click(ctrlC);
expect(api.focused()).toBe(1);
});
it("renders disabled until the terminal reports its input API", () => {
render();
for (const button of screen.getAllByRole("button")) {
expect((button as HTMLButtonElement).disabled).toBe(true);
}
});
it("exposes the keys as an accessible toolbar", () => {
const api = mockApi();
render();
expect(screen.getByRole("toolbar", { name: "Touches du terminal" })).toBeTruthy();
});
});