import { describe, expect, it } from "vitest"; import { extractCodexPromptPreview, formatSessionMessagePreview, groupSessionsByProviderAndDirectory, shouldHideCodexMessageFromToc, } from "@/components/sessions/utils"; import type { SessionMeta } from "@/types"; describe("session utils", () => { it("extracts Codex VS Code prompts after the request marker", () => { const content = [ "# Context from my IDE setup:", "", "## Active file: src/main.ts", "", "## My request for Codex:", "Fix the session title preview", ].join("\n"); expect(extractCodexPromptPreview(content)).toBe( "Fix the session title preview", ); }); it("extracts inline Codex VS Code prompts", () => { const content = [ "# Context from my IDE setup:", "", "## My request for Codex: Fix the TOC preview", ].join("\n"); expect(extractCodexPromptPreview(content)).toBe("Fix the TOC preview"); }); it("ignores marker mentions before the Codex request heading", () => { const content = [ "# Context from my IDE setup:", "", "## Active selection:", "My request for Codex: not the prompt", "", "## My request for Codex:", "Use the real request heading", ].join("\n"); expect(extractCodexPromptPreview(content)).toBe( "Use the real request heading", ); }); it("uses the last request heading when the selection contains one", () => { const content = [ "# Context from my IDE setup:", "", "## Active selection: docs/codex-format.md:10-14", "## My request for Codex:", "selected document content, not the real request", "", "## My request for Codex:", "the real injected request", ].join("\n"); expect(extractCodexPromptPreview(content)).toBe( "the real injected request", ); }); // Known limitation: the IDE marker is matched purely by text, so a // "## My request for Codex:" line inside the real request body is treated as // a new boundary and only the trailing part is kept. Pinning this documents // the best-effort behavior; fully fixing it needs structured IDE section data // that the Codex VS Code context does not provide. it("keeps only the trailing part when the request body repeats the heading", () => { const content = [ "# Context from my IDE setup:", "", "## Active file: foo.ts", "", "## My request for Codex:", "Document the format, for example:", "## My request for Codex:", "and the rest follows.", ].join("\n"); expect(extractCodexPromptPreview(content)).toBe("and the rest follows."); }); it("does not extract from ordinary messages that mention the marker", () => { const content = "Please explain the phrase My request for Codex."; expect(extractCodexPromptPreview(content)).toBe(content); }); it("hides Codex context messages without user prompts from the TOC", () => { expect( shouldHideCodexMessageFromToc("# AGENTS.md instructions for F:/project"), ).toBe(true); expect( shouldHideCodexMessageFromToc( "\nF:/project", ), ).toBe(true); expect(shouldHideCodexMessageFromToc("# Context from my IDE setup:")).toBe( true, ); expect( shouldHideCodexMessageFromToc( "# Context from my IDE setup:\n\n## My request for Codex:\nFix it", ), ).toBe(false); }); it("formats message previews with truncation", () => { expect(formatSessionMessagePreview("short message")).toBe("short message"); expect(formatSessionMessagePreview("a".repeat(51))).toBe( `${"a".repeat(50)}...`, ); }); it("groups sessions by provider and project directory", () => { const sessions: SessionMeta[] = [ { providerId: "codex", sessionId: "codex-1", projectDir: "/workspace/app", }, { providerId: "codex", sessionId: "codex-2", projectDir: "/workspace/app", }, { providerId: "claude", sessionId: "claude-1", projectDir: "/workspace/docs", }, ]; const groups = groupSessionsByProviderAndDirectory(sessions, "未知目录"); expect(groups).toHaveLength(2); expect(groups[0].providerId).toBe("codex"); expect(groups[0].sessions.map((session) => session.sessionId)).toEqual([ "codex-1", "codex-2", ]); expect(groups[0].directories).toHaveLength(1); expect(groups[0].directories[0]).toMatchObject({ projectDir: "/workspace/app", label: "app", }); expect( groups[0].directories[0].sessions.map((session) => session.sessionId), ).toEqual(["codex-1", "codex-2"]); expect(groups[1].providerId).toBe("claude"); expect(groups[1].directories[0].label).toBe("docs"); }); it("uses an unknown directory group for sessions without project directories", () => { const sessions: SessionMeta[] = [ { providerId: "codex", sessionId: "codex-1", projectDir: null, }, { providerId: "codex", sessionId: "codex-2", projectDir: " ", }, ]; const groups = groupSessionsByProviderAndDirectory(sessions, "未知目录"); expect(groups).toHaveLength(1); expect(groups[0].directories).toHaveLength(1); expect(groups[0].directories[0]).toMatchObject({ projectDir: null, label: "未知目录", }); expect( groups[0].directories[0].sessions.map((session) => session.sessionId), ).toEqual(["codex-1", "codex-2"]); }); it("preserves filtered session order inside provider and directory groups", () => { const sessions: SessionMeta[] = [ { providerId: "codex", sessionId: "newest", projectDir: "/workspace/app", lastActiveAt: 30, }, { providerId: "codex", sessionId: "middle", projectDir: "/workspace/docs", lastActiveAt: 20, }, { providerId: "codex", sessionId: "oldest", projectDir: "/workspace/app", lastActiveAt: 10, }, ]; const groups = groupSessionsByProviderAndDirectory(sessions, "未知目录"); expect(groups[0].sessions.map((session) => session.sessionId)).toEqual([ "newest", "middle", "oldest", ]); expect(groups[0].directories.map((group) => group.label)).toEqual([ "app", "docs", ]); expect( groups[0].directories[0].sessions.map((session) => session.sessionId), ).toEqual(["newest", "oldest"]); }); });