Files
cline/apps/cli/src/commands/history-command.test.ts
T
Bee 55ccbb7d51 fix(cli): remove rendering multiple views in one Bun process (#10936)
* fix(cli): open history in the existing TUI

* refactor(cli): clarify history TUI startup target

* feat(cli): add history actions to TUI

* fix(cli): avoid empty session when resuming history

* fix(cli): fail history delete without session id

* fix(cli): dispatch resume hook from history picker
2026-07-30 11:16:51 -07:00

95 lines
2.7 KiB
TypeScript

import { Command } from "commander";
import { beforeEach, describe, expect, it, vi } from "vitest";
const historyMocks = vi.hoisted(() => ({
runHistoryDelete: vi.fn(async () => 0),
runHistoryExport: vi.fn(async () => 0),
runHistoryList: vi.fn(async () => 0),
runHistoryUpdate: vi.fn(async () => 0),
}));
vi.mock("./history", () => historyMocks);
import { registerHistoryCommand } from "./history-command";
function createHarness(isInteractiveTTY: boolean) {
const program = new Command()
.exitOverride()
.option("--json", "Output as JSON");
program.configureOutput({
writeOut: vi.fn(),
writeErr: vi.fn(),
});
const io = {
writeln: vi.fn(),
writeErr: vi.fn(),
};
const setExitCode = vi.fn();
const setStartupTarget = vi.fn();
registerHistoryCommand({
program,
io,
setExitCode,
setStartupTarget,
isInteractiveTTY: () => isInteractiveTTY,
});
return { program, io, setExitCode, setStartupTarget };
}
describe("registerHistoryCommand", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("opens the in-app history picker for an interactive text terminal", async () => {
const { program, setExitCode, setStartupTarget } = createHarness(true);
await program.parseAsync(["history"], { from: "user" });
expect(setStartupTarget).toHaveBeenCalledOnce();
expect(setStartupTarget).toHaveBeenCalledWith("history");
expect(historyMocks.runHistoryList).not.toHaveBeenCalled();
expect(setExitCode).not.toHaveBeenCalled();
});
it("keeps explicit JSON output non-interactive even when a TTY is attached", async () => {
const { program, io, setExitCode, setStartupTarget } = createHarness(true);
await program.parseAsync(["history", "--json"], { from: "user" });
expect(setStartupTarget).not.toHaveBeenCalled();
expect(historyMocks.runHistoryList).toHaveBeenCalledWith({
limit: 50,
outputMode: "json",
io,
});
expect(setExitCode).toHaveBeenCalledWith(0);
});
it("prints text history when no interactive terminal is attached", async () => {
const { program, io, setExitCode, setStartupTarget } = createHarness(false);
await program.parseAsync(["history", "--limit", "12"], { from: "user" });
expect(setStartupTarget).not.toHaveBeenCalled();
expect(historyMocks.runHistoryList).toHaveBeenCalledWith({
limit: 12,
outputMode: "text",
io,
});
expect(setExitCode).toHaveBeenCalledWith(0);
});
it("returns an error when delete is missing --session-id", async () => {
const { program, io, setExitCode } = createHarness(false);
await program.parseAsync(["history", "delete"], { from: "user" });
expect(io.writeErr).toHaveBeenCalledWith(
"history delete requires --session-id <id>",
);
expect(historyMocks.runHistoryDelete).not.toHaveBeenCalled();
expect(setExitCode).toHaveBeenCalledWith(1);
});
});