308aada5f9
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
234 lines
7.5 KiB
TypeScript
234 lines
7.5 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import { buildContextItems } from "./chat-context-items";
|
|
import type { AgentMessageComment } from "@/lib/state/slices/comments";
|
|
import type { FileContextItem } from "@/lib/types/context";
|
|
|
|
const comment: AgentMessageComment = {
|
|
id: "message-comment-1",
|
|
sessionId: "session-1",
|
|
source: "agent-message",
|
|
messageId: "reply-1",
|
|
selectedText: "selected answer",
|
|
anchor: {
|
|
messageId: "reply-1",
|
|
start: 0,
|
|
end: 15,
|
|
selectedText: "selected answer",
|
|
prefix: "",
|
|
suffix: " continues",
|
|
},
|
|
text: "Please clarify this.",
|
|
createdAt: "2026-07-20T00:00:00Z",
|
|
status: "pending",
|
|
};
|
|
|
|
describe("buildContextItems agent-message comments", () => {
|
|
it("creates one removable context item for pending message comments", () => {
|
|
const items = buildContextItems({
|
|
planContextEnabled: false,
|
|
contextFiles: [],
|
|
resolvedSessionId: "session-1",
|
|
removeContextFile: vi.fn(),
|
|
unpinFile: vi.fn(),
|
|
addPlan: vi.fn(),
|
|
promptsMap: new Map(),
|
|
pendingCommentsByFile: {},
|
|
handleRemoveCommentFile: vi.fn(),
|
|
handleRemoveComment: vi.fn(),
|
|
planComments: [],
|
|
handleClearPlanComments: vi.fn(),
|
|
pendingPRFeedback: [],
|
|
handleRemovePRFeedback: vi.fn(),
|
|
handleClearPRFeedback: vi.fn(),
|
|
walkthroughComments: [],
|
|
handleRemoveWalkthroughComment: vi.fn(),
|
|
handleClearWalkthroughComments: vi.fn(),
|
|
messageComments: [comment],
|
|
handleClearMessageComments: vi.fn(),
|
|
taskId: "task-1",
|
|
} as never);
|
|
|
|
const item = items.find((candidate) => candidate.kind === "agent-message-comment");
|
|
expect(item).toMatchObject({
|
|
kind: "agent-message-comment",
|
|
label: "1 message comment",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("buildContextItems task plan comments", () => {
|
|
it("opens the Plan but cannot remove shared comments from a session composer", () => {
|
|
const addPlan = vi.fn();
|
|
const clear = vi.fn();
|
|
const items = buildContextItems({
|
|
planContextEnabled: false,
|
|
contextFiles: [],
|
|
resolvedSessionId: "session-secondary",
|
|
removeContextFile: vi.fn(),
|
|
unpinFile: vi.fn(),
|
|
addPlan,
|
|
promptsMap: new Map(),
|
|
pendingCommentsByFile: {},
|
|
handleRemoveCommentFile: vi.fn(),
|
|
handleRemoveComment: vi.fn(),
|
|
planComments: [{ id: "comment-1", version: 1 }],
|
|
handleClearPlanComments: clear,
|
|
pendingPRFeedback: [],
|
|
handleRemovePRFeedback: vi.fn(),
|
|
handleClearPRFeedback: vi.fn(),
|
|
walkthroughComments: [],
|
|
handleRemoveWalkthroughComment: vi.fn(),
|
|
handleClearWalkthroughComments: vi.fn(),
|
|
messageComments: [],
|
|
handleClearMessageComments: vi.fn(),
|
|
taskId: "task-1",
|
|
} as never);
|
|
|
|
const item = items.find((candidate) => candidate.kind === "plan-comment");
|
|
expect(item).toMatchObject({ kind: "plan-comment", label: "1 plan comment", onOpen: addPlan });
|
|
expect(item?.onRemove).toBeUndefined();
|
|
expect(clear).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe("buildContextItems task preview feedback", () => {
|
|
it("opens the task-scoped collection from the context chip", () => {
|
|
const open = vi.fn();
|
|
const items = buildContextItems({
|
|
planContextEnabled: false,
|
|
contextFiles: [],
|
|
resolvedSessionId: "session-secondary",
|
|
removeContextFile: vi.fn(),
|
|
unpinFile: vi.fn(),
|
|
addPlan: vi.fn(),
|
|
promptsMap: new Map(),
|
|
pendingCommentsByFile: {},
|
|
handleRemoveCommentFile: vi.fn(),
|
|
handleRemoveComment: vi.fn(),
|
|
planComments: [],
|
|
handleClearPlanComments: vi.fn(),
|
|
previewFeedback: [
|
|
{
|
|
id: "preview-1",
|
|
version: 2,
|
|
comment: "Increase contrast",
|
|
kind: "element",
|
|
source_label: "Checkout preview",
|
|
page_route: "/cart",
|
|
},
|
|
],
|
|
pendingPRFeedback: [],
|
|
handleRemovePRFeedback: vi.fn(),
|
|
handleClearPRFeedback: vi.fn(),
|
|
walkthroughComments: [],
|
|
handleRemoveWalkthroughComment: vi.fn(),
|
|
handleClearWalkthroughComments: vi.fn(),
|
|
messageComments: [],
|
|
handleClearMessageComments: vi.fn(),
|
|
taskId: "task-1",
|
|
onOpenPreviewFeedback: open,
|
|
} as never);
|
|
|
|
const item = items.find((candidate) => candidate.kind === "preview-feedback");
|
|
expect(item).toMatchObject({
|
|
kind: "preview-feedback",
|
|
label: "1 preview feedback item",
|
|
onOpen: open,
|
|
});
|
|
expect(item?.onRemove).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("buildContextItems file and directory context", () => {
|
|
function buildItems(contextFiles: never[]) {
|
|
return buildContextItems({
|
|
planContextEnabled: false,
|
|
contextFiles,
|
|
resolvedSessionId: "session-1",
|
|
removeContextFile: vi.fn(),
|
|
unpinFile: vi.fn(),
|
|
addPlan: vi.fn(),
|
|
promptsMap: new Map(),
|
|
onOpenFile: vi.fn(),
|
|
pendingCommentsByFile: {},
|
|
handleRemoveCommentFile: vi.fn(),
|
|
handleRemoveComment: vi.fn(),
|
|
planComments: [],
|
|
handleClearPlanComments: vi.fn(),
|
|
pendingPRFeedback: [],
|
|
handleRemovePRFeedback: vi.fn(),
|
|
handleClearPRFeedback: vi.fn(),
|
|
walkthroughComments: [],
|
|
handleRemoveWalkthroughComment: vi.fn(),
|
|
handleClearWalkthroughComments: vi.fn(),
|
|
messageComments: [],
|
|
handleClearMessageComments: vi.fn(),
|
|
taskId: "task-1",
|
|
});
|
|
}
|
|
|
|
it("marks directory items and does not expose file-open behavior", () => {
|
|
const items = buildItems([
|
|
{ path: "src", name: "src", isDirectory: true },
|
|
{ path: "src/index.ts", name: "index.ts" },
|
|
] as never[]);
|
|
|
|
expect(items).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ kind: "file", path: "src", isDirectory: true }),
|
|
expect.objectContaining({ kind: "file", path: "src/index.ts", isDirectory: false }),
|
|
]),
|
|
);
|
|
const fileItems = items.filter((item): item is FileContextItem => item.kind === "file");
|
|
const directoryItem = fileItems.find((item) => item.id === "src");
|
|
const fileItem = fileItems.find((item) => item.id === "src/index.ts");
|
|
expect(directoryItem?.onOpen).toBeUndefined();
|
|
expect(fileItem?.onOpen).toEqual(expect.any(Function));
|
|
});
|
|
});
|
|
|
|
it.each(["api", ""])("opens whole-file composer context in repository %j", (repositoryName) => {
|
|
const onOpenFileAtLine = vi.fn();
|
|
const items = buildContextItems({
|
|
planContextEnabled: false,
|
|
contextFiles: [],
|
|
resolvedSessionId: "session-1",
|
|
removeContextFile: vi.fn(),
|
|
unpinFile: vi.fn(),
|
|
addPlan: vi.fn(),
|
|
promptsMap: new Map(),
|
|
pendingCommentsByFile: {
|
|
file: [
|
|
{
|
|
source: "review-file",
|
|
id: "whole",
|
|
sessionId: "session-1",
|
|
repositoryName,
|
|
filePath: "README.md",
|
|
text: "Feedback",
|
|
status: "pending",
|
|
createdAt: "now",
|
|
},
|
|
],
|
|
},
|
|
handleRemoveCommentFile: vi.fn(),
|
|
handleRemoveComment: vi.fn(),
|
|
onOpenFileAtLine,
|
|
planComments: [],
|
|
handleClearPlanComments: vi.fn(),
|
|
pendingPRFeedback: [],
|
|
handleRemovePRFeedback: vi.fn(),
|
|
handleClearPRFeedback: vi.fn(),
|
|
walkthroughComments: [],
|
|
handleRemoveWalkthroughComment: vi.fn(),
|
|
handleClearWalkthroughComments: vi.fn(),
|
|
messageComments: [],
|
|
handleClearMessageComments: vi.fn(),
|
|
taskId: "task-1",
|
|
});
|
|
const item = items.find((candidate) => candidate.kind === "comment");
|
|
item?.onOpen?.();
|
|
expect(onOpenFileAtLine).toHaveBeenCalledWith("README.md", repositoryName);
|
|
expect(item).toHaveProperty("repositoryName", repositoryName);
|
|
});
|