test(FN-5151): complete Step 5 — add autosize regression coverage
Fusion-Task-Id: FN-5151 Fusion-Task-Lineage: 17ebfc3f-6b6d-44a6-8243-7e8b8b140057
This commit is contained in:
committed by
gsxdsm
parent
a34914ede6
commit
e0b82fda6b
@@ -1,9 +1,11 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import { userEvent } from "@testing-library/user-event";
|
||||
import { AgentOnboardingModal } from "../AgentOnboardingModal";
|
||||
|
||||
let streamHandlers: any;
|
||||
let respondCount = 0;
|
||||
const originalScrollHeightDescriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "scrollHeight");
|
||||
|
||||
vi.mock("../../api", () => ({
|
||||
startAgentOnboardingStreaming: vi.fn().mockResolvedValue({ sessionId: "onb-1" }),
|
||||
@@ -34,6 +36,16 @@ vi.mock("../../api", () => ({
|
||||
createAgent: vi.fn().mockResolvedValue({ id: "agent-1" }),
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
respondCount = 0;
|
||||
streamHandlers = undefined;
|
||||
if (originalScrollHeightDescriptor) {
|
||||
Object.defineProperty(HTMLTextAreaElement.prototype, "scrollHeight", originalScrollHeightDescriptor);
|
||||
} else {
|
||||
Reflect.deleteProperty(HTMLTextAreaElement.prototype, "scrollHeight");
|
||||
}
|
||||
});
|
||||
|
||||
describe("AgentOnboardingModal", () => {
|
||||
it("walks onboarding flow through summary and create", async () => {
|
||||
const onCreated = vi.fn();
|
||||
@@ -65,4 +77,40 @@ describe("AgentOnboardingModal", () => {
|
||||
expect(onCreated).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("autosize", () => {
|
||||
it("grows the intent textarea up to the 640px cap", async () => {
|
||||
Object.defineProperty(HTMLTextAreaElement.prototype, "scrollHeight", {
|
||||
configurable: true,
|
||||
get() {
|
||||
const value = (this as HTMLTextAreaElement).value;
|
||||
if (!value) return 24;
|
||||
if (value.includes("cap")) return 800;
|
||||
return 200;
|
||||
},
|
||||
});
|
||||
|
||||
render(
|
||||
<AgentOnboardingModal
|
||||
isOpen={true}
|
||||
onClose={vi.fn()}
|
||||
onCreated={vi.fn()}
|
||||
addToast={vi.fn()}
|
||||
existingAgents={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
const textarea = screen.getByLabelText("What do you want this agent to do?") as HTMLTextAreaElement;
|
||||
|
||||
await userEvent.type(textarea, "Draft onboarding intent");
|
||||
await waitFor(() => {
|
||||
expect(textarea.style.height).toBe("200px");
|
||||
});
|
||||
|
||||
await userEvent.type(textarea, " cap");
|
||||
await waitFor(() => {
|
||||
expect(textarea.style.height).toBe("640px");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { describe, it, expect, vi, afterEach } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { userEvent } from "@testing-library/user-event";
|
||||
import { MessageComposer } from "../MessageComposer";
|
||||
@@ -13,6 +13,16 @@ const defaultProps = {
|
||||
addToast: vi.fn(),
|
||||
};
|
||||
|
||||
const originalScrollHeightDescriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "scrollHeight");
|
||||
|
||||
afterEach(() => {
|
||||
if (originalScrollHeightDescriptor) {
|
||||
Object.defineProperty(HTMLTextAreaElement.prototype, "scrollHeight", originalScrollHeightDescriptor);
|
||||
} else {
|
||||
Reflect.deleteProperty(HTMLTextAreaElement.prototype, "scrollHeight");
|
||||
}
|
||||
});
|
||||
|
||||
describe("MessageComposer autosize", () => {
|
||||
it("grows and caps height as content wraps, then resets for short content", async () => {
|
||||
render(<MessageComposer {...defaultProps} />);
|
||||
@@ -23,7 +33,8 @@ describe("MessageComposer autosize", () => {
|
||||
get() {
|
||||
const value = (this as HTMLTextAreaElement).value;
|
||||
if (!value || value.length <= 4) return 24;
|
||||
if (value.includes("\n\n")) return 900;
|
||||
if (value.includes("\n\nline three")) return 900;
|
||||
if (value.includes("line medium")) return 500;
|
||||
return 180;
|
||||
},
|
||||
});
|
||||
@@ -31,12 +42,17 @@ describe("MessageComposer autosize", () => {
|
||||
await userEvent.type(textarea, "line one\nline two");
|
||||
await waitFor(() => {
|
||||
expect(Number.parseInt(textarea.style.height, 10)).toBeGreaterThanOrEqual(68);
|
||||
expect(Number.parseInt(textarea.style.height, 10)).toBeLessThanOrEqual(320);
|
||||
expect(Number.parseInt(textarea.style.height, 10)).toBeLessThanOrEqual(640);
|
||||
});
|
||||
|
||||
await userEvent.type(textarea, "\nline medium");
|
||||
await waitFor(() => {
|
||||
expect(textarea.style.height).toBe("500px");
|
||||
});
|
||||
|
||||
await userEvent.type(textarea, "\n\nline three");
|
||||
await waitFor(() => {
|
||||
expect(textarea.style.height).toBe("320px");
|
||||
expect(textarea.style.height).toBe("640px");
|
||||
});
|
||||
|
||||
await userEvent.clear(textarea);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
|
||||
import { userEvent } from "@testing-library/user-event";
|
||||
import { PlanningModeModal } from "../PlanningModeModal";
|
||||
import {
|
||||
@@ -79,7 +79,17 @@ vi.mock("../../hooks/useSessionLock", () => ({
|
||||
useSessionLock: () => ({ isLockedByOther: false, takeControl: vi.fn(), isLoading: false }),
|
||||
}));
|
||||
|
||||
const originalScrollHeightDescriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "scrollHeight");
|
||||
|
||||
describe("PlanningModeModal autosize", () => {
|
||||
afterEach(() => {
|
||||
if (originalScrollHeightDescriptor) {
|
||||
Object.defineProperty(HTMLTextAreaElement.prototype, "scrollHeight", originalScrollHeightDescriptor);
|
||||
} else {
|
||||
Reflect.deleteProperty(HTMLTextAreaElement.prototype, "scrollHeight");
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockConfirm.mockResolvedValue(true);
|
||||
@@ -115,7 +125,8 @@ describe("PlanningModeModal autosize", () => {
|
||||
get() {
|
||||
const value = (this as HTMLTextAreaElement).value;
|
||||
if (!value) return 24;
|
||||
if (value.split("\n").length > 5) return 800;
|
||||
if (value.includes("line 7")) return 900;
|
||||
if (value.includes("line 5")) return 500;
|
||||
return 180;
|
||||
},
|
||||
});
|
||||
@@ -123,12 +134,69 @@ describe("PlanningModeModal autosize", () => {
|
||||
await userEvent.type(textarea, "line 1\nline 2");
|
||||
await waitFor(() => {
|
||||
expect(Number.parseInt(textarea.style.height, 10)).toBeGreaterThanOrEqual(120);
|
||||
expect(Number.parseInt(textarea.style.height, 10)).toBeLessThanOrEqual(320);
|
||||
expect(Number.parseInt(textarea.style.height, 10)).toBeLessThanOrEqual(640);
|
||||
});
|
||||
|
||||
await userEvent.type(textarea, "\nline 3\nline 4\nline 5\nline 6");
|
||||
await userEvent.type(textarea, "\nline 3\nline 4\nline 5");
|
||||
await waitFor(() => {
|
||||
expect(textarea.style.height).toBe("320px");
|
||||
expect(textarea.style.height).toBe("500px");
|
||||
});
|
||||
|
||||
await userEvent.type(textarea, "\nline 6\nline 7");
|
||||
await waitFor(() => {
|
||||
expect(textarea.style.height).toBe("640px");
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps SummaryView collapsed and expanded autosize caps distinct", async () => {
|
||||
Object.defineProperty(HTMLTextAreaElement.prototype, "scrollHeight", {
|
||||
configurable: true,
|
||||
get() {
|
||||
return 900;
|
||||
},
|
||||
});
|
||||
|
||||
mockFetchAiSession.mockResolvedValueOnce({
|
||||
id: "session-complete-1",
|
||||
type: "planning",
|
||||
status: "complete",
|
||||
title: "Resume-ready planning output",
|
||||
inputPayload: JSON.stringify({ initialPlan: "Build resilient planning resume" }),
|
||||
conversationHistory: "[]",
|
||||
currentQuestion: null,
|
||||
result: JSON.stringify({
|
||||
title: "Resume-ready planning output",
|
||||
description: "Recovered summary description from persisted session",
|
||||
suggestedSize: "L",
|
||||
suggestedDependencies: ["FN-001"],
|
||||
keyDeliverables: ["Deliverable A", "Deliverable B"],
|
||||
}),
|
||||
thinkingOutput: "",
|
||||
error: null,
|
||||
projectId: null,
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
updatedAt: "2026-01-01T00:00:00.000Z",
|
||||
});
|
||||
|
||||
render(
|
||||
<PlanningModeModal
|
||||
isOpen={true}
|
||||
onClose={vi.fn()}
|
||||
onTaskCreated={vi.fn()}
|
||||
onTasksCreated={vi.fn()}
|
||||
tasks={mockTasks}
|
||||
resumeSessionId="session-complete-1"
|
||||
/>
|
||||
);
|
||||
|
||||
const description = await screen.findByDisplayValue("Recovered summary description from persisted session") as HTMLTextAreaElement;
|
||||
await waitFor(() => {
|
||||
expect(description.style.height).toBe("640px");
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText("Expand"));
|
||||
await waitFor(() => {
|
||||
expect(description.style.height).toBe("800px");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ const qualityAppTests = [
|
||||
"app/api/**/*.test.ts",
|
||||
// Representative workflow/component coverage. Exhaustive modal/view suites
|
||||
// stay available in the full `dashboard-app` project.
|
||||
"app/components/__tests__/{ActiveAgentsPanel,ActivityLogModal,AgentMentionPopup,AgentMetricsBar,AgentReflectionsTab,AgentTokenStatsPanel,App,AuthTokenRecoveryDialog,Board,board-mobile-view-switch,ChatView,ChatView.autosize,ChatView.chat-input-autosize,ChatView.default-model-icon,ChatView.draft,ChatView.rooms,ChatView.swipe-back,Column,ConfirmDialog,ConversationHistory,DashboardLoader,DevServerView.mobile,DirectoryPicker,DuplicateWarningModal,ErrorBoundary,ExecutorStatusBar,FileBrowser,FileEditor,GitHubBadge,InlineCreateCard,LoginInstructions,MemoryView,MessageComposer,MobileNavBar,NewTaskModal,NodeCard,NodeHealthDot,NodeStatusIndicator,PrChecksList,PrCreateModal,PrCreateModal.layout,ProjectCard,ProjectSelector,ProviderIcon,PrPanel,PrPanel.merge,PrPanel.reviews,QuickChatFAB,ReliabilityView,ResearchView,SecretsView,SettingsModal,SettingsModal.worktrunk,StashRecoveryView,TaskCard,TaskCard.badge-height,TaskChangesTab,TaskComments,TaskDetailModal,TaskDetailModal.github-tracking-header,TaskDetailModal.github-tracking-stale,TaskDetailModal.rebind-banner,TaskDocumentsTab,TaskForm,TaskIdIntegrityBanner,TrackingRepoSelect,WorkflowResultsTab,WorktrunkInstallApprovalDetails}.test.tsx",
|
||||
"app/components/__tests__/{ActiveAgentsPanel,ActivityLogModal,AgentMentionPopup,AgentMetricsBar,AgentOnboardingModal,AgentReflectionsTab,AgentTokenStatsPanel,App,AuthTokenRecoveryDialog,Board,board-mobile-view-switch,ChatView,ChatView.autosize,ChatView.chat-input-autosize,ChatView.default-model-icon,ChatView.draft,ChatView.rooms,ChatView.swipe-back,Column,ConfirmDialog,ConversationHistory,DashboardLoader,DevServerView.mobile,DirectoryPicker,DuplicateWarningModal,ErrorBoundary,ExecutorStatusBar,FileBrowser,FileEditor,GitHubBadge,InlineCreateCard,LoginInstructions,MemoryView,MessageComposer,MessageComposer.autosize,MobileNavBar,NewTaskModal,NodeCard,NodeHealthDot,NodeStatusIndicator,PlanningModeModal.autosize,PrChecksList,PrCreateModal,PrCreateModal.layout,ProjectCard,ProjectSelector,ProviderIcon,PrPanel,PrPanel.merge,PrPanel.reviews,QuickChatFAB,ReliabilityView,ResearchView,SecretsView,SettingsModal,SettingsModal.worktrunk,StashRecoveryView,TaskCard,TaskCard.badge-height,TaskChangesTab,TaskComments,TaskDetailModal,TaskDetailModal.github-tracking-header,TaskDetailModal.github-tracking-stale,TaskDetailModal.rebind-banner,TaskDocumentsTab,TaskForm,TaskIdIntegrityBanner,TrackingRepoSelect,WorkflowResultsTab,WorktrunkInstallApprovalDetails}.test.tsx",
|
||||
// Hooks and utilities are fast, user-visible state/formatting behavior.
|
||||
"app/context/**/*.test.tsx",
|
||||
"app/hooks/__tests__/{useAgents,useAgentLogs,useAppSettings,useAuthOnboarding,useConfirm,useCurrentProject,useNodes,useNodeSettingsSync,useProjects,useQuickChat,useTasks,useTerminalSessions,useTheme,useToast,useUsageData,useViewState}.test.{ts,tsx}",
|
||||
|
||||
Reference in New Issue
Block a user