mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-16 19:00:38 +09:00
Add issue detail shortcut for comment composer
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
15b0f11275
commit
1079f21ac4
6 changed files with 164 additions and 44 deletions
|
|
@ -1,12 +1,16 @@
|
|||
// @vitest-environment jsdom
|
||||
|
||||
import { act } from "react";
|
||||
import { act, createRef, forwardRef, useImperativeHandle } 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 { IssueChatThread, resolveAssistantMessageFoldedState } from "./IssueChatThread";
|
||||
|
||||
const { markdownEditorFocusMock } = vi.hoisted(() => ({
|
||||
markdownEditorFocusMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@assistant-ui/react", () => ({
|
||||
AssistantRuntimeProvider: ({ children }: { children: ReactNode }) => <div>{children}</div>,
|
||||
ThreadPrimitive: {
|
||||
|
|
@ -48,7 +52,7 @@ vi.mock("./MarkdownBody", () => ({
|
|||
}));
|
||||
|
||||
vi.mock("./MarkdownEditor", () => ({
|
||||
MarkdownEditor: ({
|
||||
MarkdownEditor: forwardRef(({
|
||||
value = "",
|
||||
onChange,
|
||||
placeholder,
|
||||
|
|
@ -60,16 +64,22 @@ vi.mock("./MarkdownEditor", () => ({
|
|||
placeholder?: string;
|
||||
className?: string;
|
||||
contentClassName?: string;
|
||||
}) => (
|
||||
<textarea
|
||||
aria-label="Issue chat editor"
|
||||
data-class-name={className}
|
||||
data-content-class-name={contentClassName}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={(event) => onChange?.(event.target.value)}
|
||||
/>
|
||||
),
|
||||
}, ref) => {
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus: markdownEditorFocusMock,
|
||||
}));
|
||||
|
||||
return (
|
||||
<textarea
|
||||
aria-label="Issue chat editor"
|
||||
data-class-name={className}
|
||||
data-content-class-name={contentClassName}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={(event) => onChange?.(event.target.value)}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("./InlineEntitySelector", () => ({
|
||||
|
|
@ -111,6 +121,7 @@ describe("IssueChatThread", () => {
|
|||
afterEach(() => {
|
||||
container.remove();
|
||||
vi.useRealTimers();
|
||||
markdownEditorFocusMock.mockReset();
|
||||
});
|
||||
|
||||
it("drops the count heading and does not use an internal scrollbox", () => {
|
||||
|
|
@ -279,6 +290,52 @@ describe("IssueChatThread", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("exposes a composer focus handle that forwards to the editor", () => {
|
||||
const root = createRoot(container);
|
||||
const composerRef = createRef<{ focus: () => void }>();
|
||||
const requestAnimationFrameMock = vi
|
||||
.spyOn(window, "requestAnimationFrame")
|
||||
.mockImplementation((callback: FrameRequestCallback) => {
|
||||
callback(0);
|
||||
return 1;
|
||||
});
|
||||
|
||||
act(() => {
|
||||
root.render(
|
||||
<MemoryRouter>
|
||||
<IssueChatThread
|
||||
comments={[]}
|
||||
linkedRuns={[]}
|
||||
timelineEvents={[]}
|
||||
liveRuns={[]}
|
||||
onAdd={async () => {}}
|
||||
composerRef={composerRef}
|
||||
enableLiveTranscriptPolling={false}
|
||||
/>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
});
|
||||
|
||||
const composer = container.querySelector('[data-testid="issue-chat-composer"]') as HTMLDivElement | null;
|
||||
expect(composerRef.current).not.toBeNull();
|
||||
expect(composer).not.toBeNull();
|
||||
|
||||
const scrollIntoViewMock = vi.fn();
|
||||
composer!.scrollIntoView = scrollIntoViewMock;
|
||||
|
||||
act(() => {
|
||||
composerRef.current?.focus();
|
||||
});
|
||||
|
||||
expect(scrollIntoViewMock).toHaveBeenCalledWith({ behavior: "smooth", block: "end" });
|
||||
expect(markdownEditorFocusMock).toHaveBeenCalledTimes(1);
|
||||
requestAnimationFrameMock.mockRestore();
|
||||
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
it("folds chain-of-thought when the same message transitions from running to complete", () => {
|
||||
expect(resolveAssistantMessageFoldedState({
|
||||
messageId: "message-1",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue