// @vitest-environment jsdom import { act, createRef, forwardRef, StrictMode, useImperativeHandle, useState, } from "react"; import { flushSync } from "react-dom"; 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 { CommentSubmissionUnknownError } from "../lib/comment-submit-result"; import { loadDraft, preserveDraftInTab, settleDraftSubmission } from "../lib/composer-draft"; import { IssueAssigneePausedNotice, IssueChatThread, VIRTUALIZED_THREAD_ROW_THRESHOLD, canStopIssueChatRun, findLatestCommentMessageIndex, getVirtualizedMeasurementScrollAdjustment, resolveAssistantMessageFoldedState, resolveIssueChatHumanAuthor, } from "./IssueChatThread"; import type { AskUserQuestionsInteraction, RequestConfirmationInteraction, SuggestTasksInteraction, } from "../lib/issue-thread-interactions"; import { issueChatLongThreadAgentMap, issueChatLongThreadComments, issueChatLongThreadEvents, issueChatLongThreadLinkedRuns, issueChatLongThreadTranscriptsByRunId, } from "../fixtures/issueChatLongThreadFixture"; import { expiredSecretProposalInteraction } from "../fixtures/issueThreadInteractionFixtures"; import type { IssueChatLinkedRun, IssueChatTranscriptEntry, } from "../lib/issue-chat-messages"; function flushAct(callback: () => T): T { let result: T | undefined; flushSync(() => { result = callback(); }); return result as T; } 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, className, }: { children: ReactNode; className?: string; }) => { markdownBodyRenderMock({ children, className }); 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 (