144 lines
4.4 KiB
TypeScript
144 lines
4.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildSubmitMessage } from "./chat-input-area";
|
|
import { resolveStatusRowTaskId, shouldRenderChatStatusBar } from "./chat-status-bar";
|
|
import { hasPendingClarification, shouldShowProceed } from "./types";
|
|
import type { AgentMessageComment } from "@/lib/state/slices/comments";
|
|
|
|
const messageComment: AgentMessageComment = {
|
|
id: "message-comment-1",
|
|
sessionId: "session-1",
|
|
source: "agent-message",
|
|
messageId: "reply-1",
|
|
selectedText: "answer",
|
|
anchor: {
|
|
messageId: "reply-1",
|
|
start: 0,
|
|
end: 6,
|
|
selectedText: "answer",
|
|
prefix: "",
|
|
suffix: "",
|
|
},
|
|
text: "Please make this more precise.",
|
|
createdAt: "2026-07-20T00:00:00Z",
|
|
status: "pending",
|
|
};
|
|
|
|
describe("buildSubmitMessage agent message comments", () => {
|
|
it("includes selected response context while preserving ordinary prose", () => {
|
|
const result = buildSubmitMessage({
|
|
message: "Continue from here.",
|
|
pendingPRFeedback: [],
|
|
planComments: [],
|
|
messageComments: [messageComment],
|
|
});
|
|
|
|
expect(result).toContain("### Agent Message Comments");
|
|
expect(result).toContain("> answer");
|
|
expect(result).toContain("Continue from here.");
|
|
});
|
|
});
|
|
|
|
describe("buildSubmitMessage plan comment ownership", () => {
|
|
it("leaves task-owned plan comments out of client-formatted Markdown", () => {
|
|
const result = buildSubmitMessage({
|
|
message: "Please continue.",
|
|
pendingPRFeedback: [],
|
|
planComments: [
|
|
{
|
|
id: "plan-comment-1",
|
|
sessionId: "",
|
|
taskId: "task-1",
|
|
planId: "plan-1",
|
|
version: 2,
|
|
source: "plan",
|
|
text: "Split this step.",
|
|
selectedText: "Large step",
|
|
createdAt: "2026-09-02T00:00:00Z",
|
|
status: "pending",
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(result).toBe("Please continue.");
|
|
});
|
|
});
|
|
|
|
describe("shouldRenderChatStatusBar", () => {
|
|
it("removes empty taskless status chrome when its auto-scroll control is hidden", () => {
|
|
expect(
|
|
shouldRenderChatStatusBar({
|
|
hasTask: false,
|
|
hasTodos: false,
|
|
hasQueueChip: false,
|
|
showRightControls: false,
|
|
showProceed: false,
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("shouldShowProceed", () => {
|
|
it.each([
|
|
["not busy without a clarification", false, false, true],
|
|
["busy without a clarification", true, false, false],
|
|
["waiting for input with a pending clarification", false, true, false],
|
|
])("%s", (_state, isAgentBusy, hasPendingClarification, expected) => {
|
|
expect(shouldShowProceed("Review", isAgentBusy, hasPendingClarification)).toBe(expected);
|
|
});
|
|
});
|
|
|
|
describe("hasPendingClarification", () => {
|
|
it("retains the message-derived fallback", () => {
|
|
expect(hasPendingClarification(true, null)).toBe(true);
|
|
});
|
|
|
|
it("uses the durable session projection while messages hydrate", () => {
|
|
expect(hasPendingClarification(false, "clarification")).toBe(true);
|
|
});
|
|
|
|
it("does not treat a durable permission request as a clarification", () => {
|
|
expect(hasPendingClarification(false, "permission")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("resolveStatusRowTaskId", () => {
|
|
it("prefers the session-derived task so existing hosts are unchanged", () => {
|
|
expect(resolveStatusRowTaskId("from-session", "from-host")).toBe("from-session");
|
|
});
|
|
|
|
// The regression: a blocked chain step has no session, so the session-derived
|
|
// id is null and the status row (and with it the dependency chip) disappeared
|
|
// on exactly the tasks the chip exists to describe.
|
|
it("falls back to the host's task when the task has no session yet", () => {
|
|
expect(resolveStatusRowTaskId(null, "from-host")).toBe("from-host");
|
|
});
|
|
|
|
it("stays null when neither side knows a task", () => {
|
|
expect(resolveStatusRowTaskId(null, null)).toBeNull();
|
|
});
|
|
});
|
|
|
|
it("includes whole-file feedback with ordinary composer text", () => {
|
|
const result = buildSubmitMessage({
|
|
message: "Continue.",
|
|
pendingPRFeedback: [],
|
|
planComments: [],
|
|
reviewComments: [
|
|
{
|
|
id: "file",
|
|
source: "review-file",
|
|
sessionId: "s",
|
|
repositoryName: "api",
|
|
filePath: "README.md",
|
|
text: "Split this file",
|
|
createdAt: "now",
|
|
status: "pending",
|
|
},
|
|
],
|
|
});
|
|
expect(result).toContain("**api/README.md**");
|
|
expect(result).toContain("> Split this file");
|
|
expect(result).toContain("Continue.");
|
|
expect(result).not.toContain("undefined");
|
|
});
|