166 lines
6.9 KiB
TypeScript
166 lines
6.9 KiB
TypeScript
import { describe, expect, it, vi } from "bun:test";
|
|
import { AssistantMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/assistant-message";
|
|
import { InputController } from "@oh-my-pi/pi-coding-agent/modes/controllers/input-controller";
|
|
import type { InteractiveModeContext } from "@oh-my-pi/pi-coding-agent/modes/types";
|
|
|
|
describe("InputController thinking visibility", () => {
|
|
it("keeps pre-stream pending transcript content mounted when Ctrl+T toggles thinking blocks", () => {
|
|
const pendingUserMessage = { kind: "pending-user" };
|
|
const loadingIndicator = { kind: "loading" };
|
|
const assistant = new AssistantMessageComponent();
|
|
const setHideThinkingBlock = vi.spyOn(assistant, "setHideThinkingBlock");
|
|
const resetDisplay = vi.fn();
|
|
const clear = vi.fn();
|
|
const addChild = vi.fn();
|
|
const rebuildChatFromMessages = vi.fn();
|
|
const set = vi.fn();
|
|
const showStatus = vi.fn();
|
|
const children = [pendingUserMessage, assistant, loadingIndicator];
|
|
const chatContainer = { children, clear, addChild };
|
|
const ctx = {
|
|
hideThinkingBlock: false,
|
|
effectiveHideThinkingBlock: false,
|
|
settings: { set },
|
|
session: { agent: { hideThinkingSummary: false }, thinkingLevel: "high" },
|
|
chatContainer,
|
|
streamingComponent: undefined,
|
|
streamingMessage: undefined,
|
|
rebuildChatFromMessages,
|
|
showStatus,
|
|
ui: { resetDisplay },
|
|
} as unknown as InteractiveModeContext;
|
|
|
|
new InputController(ctx).toggleThinkingBlockVisibility();
|
|
|
|
expect(ctx.hideThinkingBlock).toBe(true);
|
|
expect(set).toHaveBeenCalledWith("hideThinkingBlock", true);
|
|
expect(ctx.session.agent.hideThinkingSummary).toBe(false);
|
|
expect(chatContainer.children).toEqual([pendingUserMessage, assistant, loadingIndicator]);
|
|
expect(clear).not.toHaveBeenCalled();
|
|
expect(addChild).not.toHaveBeenCalled();
|
|
expect(rebuildChatFromMessages).not.toHaveBeenCalled();
|
|
expect(setHideThinkingBlock).toHaveBeenCalledWith(true);
|
|
expect(resetDisplay).toHaveBeenCalledTimes(1);
|
|
expect(showStatus).toHaveBeenCalledWith("Thinking blocks: hidden");
|
|
});
|
|
|
|
it("refuses to toggle and informs the user when thinking level is off", () => {
|
|
// When thinking is "off", effectiveHideThinkingBlock is true even if the
|
|
// user's hideThinkingBlock setting is false. The toggle should refuse
|
|
// instead of silently no-op'ing or corrupting the setting.
|
|
const assistant = new AssistantMessageComponent();
|
|
const setHideThinkingBlock = vi.spyOn(assistant, "setHideThinkingBlock");
|
|
const set = vi.fn();
|
|
const showStatus = vi.fn();
|
|
const resetDisplay = vi.fn();
|
|
const ctx = {
|
|
hideThinkingBlock: false,
|
|
effectiveHideThinkingBlock: true, // thinking is off → effective is true
|
|
settings: { set },
|
|
session: { agent: { hideThinkingSummary: false }, thinkingLevel: "off" },
|
|
chatContainer: { children: [assistant], clear: vi.fn(), addChild: vi.fn() },
|
|
streamingComponent: undefined,
|
|
streamingMessage: undefined,
|
|
showStatus,
|
|
ui: { resetDisplay },
|
|
} as unknown as InteractiveModeContext;
|
|
|
|
new InputController(ctx).toggleThinkingBlockVisibility();
|
|
|
|
// Setting was not changed, components were not updated, no reset.
|
|
expect(ctx.hideThinkingBlock).toBe(false);
|
|
expect(set).not.toHaveBeenCalled();
|
|
expect(setHideThinkingBlock).not.toHaveBeenCalled();
|
|
expect(resetDisplay).not.toHaveBeenCalled();
|
|
expect(showStatus).toHaveBeenCalledWith("Thinking is off — enable thinking to show blocks");
|
|
});
|
|
|
|
it("allows toggling when thinking is off after reasoning content was received", () => {
|
|
const assistant = new AssistantMessageComponent();
|
|
const setHideThinkingBlock = vi.spyOn(assistant, "setHideThinkingBlock");
|
|
const set = vi.fn();
|
|
const showStatus = vi.fn();
|
|
const resetDisplay = vi.fn();
|
|
const ctx = {
|
|
hideThinkingBlock: false,
|
|
effectiveHideThinkingBlock: false,
|
|
hasDisplayableThinkingContent: true,
|
|
settings: { set },
|
|
session: { agent: { hideThinkingSummary: false }, thinkingLevel: "off" },
|
|
chatContainer: { children: [assistant], clear: vi.fn(), addChild: vi.fn() },
|
|
streamingComponent: undefined,
|
|
streamingMessage: undefined,
|
|
showStatus,
|
|
ui: { resetDisplay },
|
|
} as unknown as InteractiveModeContext;
|
|
|
|
new InputController(ctx).toggleThinkingBlockVisibility();
|
|
|
|
expect(ctx.hideThinkingBlock).toBe(true);
|
|
expect(set).toHaveBeenCalledWith("hideThinkingBlock", true);
|
|
expect(setHideThinkingBlock).toHaveBeenCalledWith(true);
|
|
expect(resetDisplay).toHaveBeenCalledTimes(1);
|
|
expect(showStatus).toHaveBeenCalledWith("Thinking blocks: hidden");
|
|
});
|
|
|
|
it("refuses to toggle when the focused view session has thinking off", () => {
|
|
const assistant = new AssistantMessageComponent();
|
|
const setHideThinkingBlock = vi.spyOn(assistant, "setHideThinkingBlock");
|
|
const set = vi.fn();
|
|
const showStatus = vi.fn();
|
|
const resetDisplay = vi.fn();
|
|
const ctx = {
|
|
hideThinkingBlock: false,
|
|
effectiveHideThinkingBlock: true,
|
|
settings: { set },
|
|
session: { agent: { hideThinkingSummary: false }, thinkingLevel: "high" },
|
|
viewSession: { thinkingLevel: "off" },
|
|
chatContainer: { children: [assistant], clear: vi.fn(), addChild: vi.fn() },
|
|
streamingComponent: undefined,
|
|
streamingMessage: undefined,
|
|
showStatus,
|
|
ui: { resetDisplay },
|
|
} as unknown as InteractiveModeContext;
|
|
|
|
new InputController(ctx).toggleThinkingBlockVisibility();
|
|
|
|
expect(ctx.hideThinkingBlock).toBe(false);
|
|
expect(set).not.toHaveBeenCalled();
|
|
expect(setHideThinkingBlock).not.toHaveBeenCalled();
|
|
expect(resetDisplay).not.toHaveBeenCalled();
|
|
expect(showStatus).toHaveBeenCalledWith("Thinking is off — enable thinking to show blocks");
|
|
});
|
|
|
|
it("refuses to toggle when thinking is off even if hideThinkingBlock is already true", () => {
|
|
// The persisted preference may already be true from a prior session
|
|
// where thinking was on. With thinking off, effectiveHideThinkingBlock
|
|
// is true regardless, so any toggle is a no-op — guard it rather than
|
|
// flipping the persisted preference back to false.
|
|
const assistant = new AssistantMessageComponent();
|
|
const setHideThinkingBlock = vi.spyOn(assistant, "setHideThinkingBlock");
|
|
const set = vi.fn();
|
|
const showStatus = vi.fn();
|
|
const resetDisplay = vi.fn();
|
|
const ctx = {
|
|
hideThinkingBlock: true,
|
|
effectiveHideThinkingBlock: true, // thinking is off → effective is true
|
|
settings: { set },
|
|
session: { agent: { hideThinkingSummary: false }, thinkingLevel: "off" },
|
|
chatContainer: { children: [assistant], clear: vi.fn(), addChild: vi.fn() },
|
|
streamingComponent: undefined,
|
|
streamingMessage: undefined,
|
|
showStatus,
|
|
ui: { resetDisplay },
|
|
} as unknown as InteractiveModeContext;
|
|
|
|
new InputController(ctx).toggleThinkingBlockVisibility();
|
|
|
|
// Persisted preference unchanged, no component updates, no reset.
|
|
expect(ctx.hideThinkingBlock).toBe(true);
|
|
expect(set).not.toHaveBeenCalled();
|
|
expect(setHideThinkingBlock).not.toHaveBeenCalled();
|
|
expect(resetDisplay).not.toHaveBeenCalled();
|
|
expect(showStatus).toHaveBeenCalledWith("Thinking is off — enable thinking to show blocks");
|
|
});
|
|
});
|