// @vitest-environment jsdom import { createRoot } from "react-dom/client"; import { flushSync } from "react-dom"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { Issue } from "@paperclipai/shared"; import { InboxIssueMetaLeading, InboxIssueTrailingColumns, IssueColumnPicker, issueColumnDescription, } from "./IssueColumns"; import { TooltipProvider } from "@/components/ui/tooltip"; // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; function act(callback: () => void): void { flushSync(callback); } function makeIssue(overrides: Partial): Issue { return { id: "issue-id", identifier: "PAP-1", status: "in_progress", blockerAttention: false, ...overrides, } as unknown as Issue; } let container: HTMLDivElement | null = null; let root: ReturnType | null = null; afterEach(() => { if (root) act(() => root!.unmount()); root = null; container?.remove(); container = null; }); function renderLeading(element: React.ReactElement): string { container = document.createElement("div"); document.body.appendChild(container); root = createRoot(container); act(() => root!.render({element})); return container.textContent ?? ""; } describe("IssueColumnPicker presentation options", () => { it("offers date group separators as a toggle in the Columns menu", () => { const onToggleDateGroupSeparators = vi.fn(); renderLeading( undefined} showDateGroupSeparators onToggleDateGroupSeparators={onToggleDateGroupSeparators} onResetColumns={() => undefined} title="Choose which task columns stay visible" iconOnly rowPresentation="task" />, ); const columnsButton = container?.querySelector('button[title="Columns"]'); expect(columnsButton).not.toBeNull(); act(() => columnsButton?.dispatchEvent(new MouseEvent("pointerdown", { bubbles: true }))); const option = Array.from(document.body.querySelectorAll('[role="menuitemcheckbox"]')) .find((item) => item.textContent?.includes("Date group separators")); expect(option?.getAttribute("aria-checked")).toBe("true"); act(() => option?.dispatchEvent(new MouseEvent("click", { bubbles: true }))); expect(onToggleDateGroupSeparators).toHaveBeenCalledWith(false); }); }); describe("InboxIssueMetaLeading live state", () => { it("describes the canonical identifier at the trailing edge", () => { expect(issueColumnDescription("id", "task")).toContain("trailing edge"); expect(issueColumnDescription("id", "legacy")).not.toContain("trailing edge"); }); it("shows the own Live chip for a running issue and never the subtree chip", () => { const text = renderLeading( , ); expect(text).toContain("Live"); expect(text).not.toContain("live below"); }); it("shows the distinct subtree chip for a done parent with live descendants", () => { const text = renderLeading( , ); // The done parent must NOT borrow the running child's "Live" chip. expect(text).toContain("2 live below"); expect(text).not.toMatch(/(^|[^a-z])Live([^a-z]|$)/); }); it("can suppress the subtree chip when the status glyph already carries descendant liveness", () => { const text = renderLeading( , ); expect(text).not.toContain("live below"); }); it("renders no live treatment when the issue and its subtree are idle", () => { const text = renderLeading( , ); expect(text).not.toContain("Live"); expect(text).not.toContain("live below"); }); }); describe("InboxIssueTrailingColumns attribution", () => { it("renders a kicked off by column for agent creators with a character identity", () => { const text = renderLeading( , ); expect(text).toContain("CodexCoder"); expect(container?.querySelector('[data-slot="agent-avatar"]')).not.toBeNull(); }); it("renders a kicked off by column for user creators", () => { const text = renderLeading( , ); expect(text).toContain("Riley Board"); expect(container?.querySelector('[data-shape="circle"]')).not.toBeNull(); }); it("attributes an agent-created issue to the transitive responsible user (circle, not agent square)", () => { const text = renderLeading( , ); // The responsible user wins over the creating agent. expect(text).toContain("Morgan Product"); expect(container?.querySelector('[data-shape="circle"]')).not.toBeNull(); expect(container?.querySelector('[data-slot="agent-avatar"]')).toBeNull(); }); it("surfaces the responsible user for a routine execution with no creator", () => { const text = renderLeading( , ); expect(text).toContain("Morgan Product"); expect(text).not.toContain("Unknown"); }); });