import { describe, expect, it } from "vitest"; import { TerminalLocalEcho, resolveLocalEchoMode, } from "../../../src/frontend/lib/terminal-local-echo"; describe("TerminalLocalEcho", () => { it("renders immediately and suppresses the matching remote echo", () => { const echo = new TerminalLocalEcho("on"); expect(echo.handleInput("a")).toBe("a"); expect(echo.handleOutput("a")).toBe(""); }); it("rolls back a prediction when remote output differs", () => { const echo = new TerminalLocalEcho("on"); echo.handleInput("a"); expect(echo.handleOutput("z")).toBe("\x1b[1D\x1b[1Xz"); }); it("only erases the predicted cells during rollback", () => { const echo = new TerminalLocalEcho("on"); echo.handleInput("a"); echo.handleInput("b"); expect(echo.handleOutput("\x1b[C")).toBe("\x1b[2D\x1b[2X\x1b[C"); }); it.each([ ["split prompt", ["Pass", "word: "]], ["sudo prompt", ["[sudo] password for alice: "]], ["ssh-keygen prompt", ["Enter passphrase (empty for no passphrase): "]], ["passwd prompt", ["New password: "]], ])("does not expose input after a %s", (_name, chunks) => { const echo = new TerminalLocalEcho("on"); for (const chunk of chunks) expect(echo.handleOutput(chunk)).toBe(chunk); expect(echo.handleInput("s")).toBe(""); }); it("does not treat ordinary password text as a hidden-input prompt", () => { const echo = new TerminalLocalEcho("on"); expect(echo.handleOutput("Your password has expired\r\n$ ")).toBe( "Your password has expired\r\n$ ", ); expect(echo.handleInput("s")).toBe("s"); }); it("does not predict control input, paste, or wide characters", () => { const echo = new TerminalLocalEcho("on"); expect(echo.handleInput("\t")).toBe(""); expect(echo.handleInput("paste")).toBe(""); expect(echo.handleInput("界")).toBe(""); }); it("enables automatic prediction after repeated slow echoes", () => { let now = 0; const echo = new TerminalLocalEcho("auto", () => now, 100); expect(echo.handleInput("a")).toBe(""); now = 150; expect(echo.handleOutput("a")).toBe("a"); expect(echo.handleInput("b")).toBe(""); now = 300; expect(echo.handleOutput("b")).toBe("b"); expect(echo.handleInput("c")).toBe("c"); }); it("uses an explicit host mode before the global mode", () => { expect(resolveLocalEchoMode("off", "on")).toBe("off"); expect(resolveLocalEchoMode("default", "on")).toBe("on"); expect(resolveLocalEchoMode(undefined, null)).toBe("auto"); }); });