// @vitest-environment jsdom import { act, createRef, forwardRef, useImperativeHandle, useState } from "react"; import type { ReactNode } from "react"; import { createRoot } from "react-dom/client"; import { MemoryRouter } from "react-router-dom"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { Agent } from "@paperclipai/shared"; import { IssueChatThread, VIRTUALIZED_THREAD_ROW_THRESHOLD, canStopIssueChatRun, findLatestCommentMessageIndex, resolveAssistantMessageFoldedState, resolveIssueChatHumanAuthor, } from "./IssueChatThread"; import { ToastProvider } from "../context/ToastContext"; import { ToastViewport } from "./ToastViewport"; import type { AskUserQuestionsInteraction, RequestConfirmationInteraction, SuggestTasksInteraction, } from "../lib/issue-thread-interactions"; import { issueChatLongThreadAgentMap, issueChatLongThreadComments, issueChatLongThreadEvents, issueChatLongThreadLinkedRuns, issueChatLongThreadTranscriptsByRunId, } from "../fixtures/issueChatLongThreadFixture"; import type { IssueChatLinkedRun, IssueChatTranscriptEntry, } from "../lib/issue-chat-messages"; function hasSmoothScrollBehavior(arg: unknown) { return typeof arg === "object" && arg !== null && "behavior" in arg && (arg as ScrollToOptions).behavior === "smooth"; } const { markdownBodyRenderMock, markdownEditorFocusMock } = vi.hoisted(() => ({ markdownBodyRenderMock: vi.fn(), markdownEditorFocusMock: vi.fn(), })); const { appendMock } = vi.hoisted(() => ({ appendMock: vi.fn(async () => undefined), })); const { captureComposerViewportSnapshotMock, restoreComposerViewportSnapshotMock, shouldPreserveComposerViewportMock, } = vi.hoisted(() => ({ captureComposerViewportSnapshotMock: vi.fn(), restoreComposerViewportSnapshotMock: vi.fn(), shouldPreserveComposerViewportMock: vi.fn(), })); vi.mock("@assistant-ui/react", () => ({ AssistantRuntimeProvider: ({ children }: { children: ReactNode }) =>
{children}
, useAui: () => ({ thread: () => ({ append: appendMock }) }), })); vi.mock("./transcript/useLiveRunTranscripts", () => ({ useLiveRunTranscripts: () => ({ transcriptByRun: new Map(), hasOutputForRun: () => false, }), })); vi.mock("../lib/issue-chat-scroll", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, captureComposerViewportSnapshot: captureComposerViewportSnapshotMock.mockImplementation(actual.captureComposerViewportSnapshot), restoreComposerViewportSnapshot: restoreComposerViewportSnapshotMock.mockImplementation(actual.restoreComposerViewportSnapshot), shouldPreserveComposerViewport: shouldPreserveComposerViewportMock.mockImplementation(actual.shouldPreserveComposerViewport), }; }); vi.mock("./MarkdownBody", () => ({ MarkdownBody: ({ children }: { children: ReactNode }) => { markdownBodyRenderMock(children); return
{children}
; }, })); vi.mock("./MarkdownEditor", () => ({ MarkdownEditor: forwardRef(({ value = "", onChange, placeholder, className, contentClassName, fileDropTarget, }: { value?: string; onChange?: (value: string) => void; placeholder?: string; className?: string; contentClassName?: string; fileDropTarget?: "editor" | "parent"; }, ref) => { useImperativeHandle(ref, () => ({ focus: markdownEditorFocusMock, })); return (