From e313bf5776052ed03f4acc185f07e194cdfd2979 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 24 Apr 2026 02:52:23 +0700 Subject: [PATCH] chore(lint): hoist test fixture arrays/objects Clears 14 react-perf/jsx-no-new-* warnings across app test files by hoisting constant fixtures to module scope or wrapping them in helper factories so they no longer appear as inline JSX prop values. --- .../app/src/components/callout-card.test.tsx | 23 ++++++++++++---- packages/app/src/components/composer.test.tsx | 5 ++-- .../app/src/components/message-input.test.tsx | 20 ++++++++------ .../components/stream-strategy-web.test.tsx | 4 ++- packages/app/src/panels/agent-panel.test.tsx | 27 ++++++++++--------- .../src/screens/new-workspace-screen.test.tsx | 13 ++++++--- 6 files changed, 59 insertions(+), 33 deletions(-) diff --git a/packages/app/src/components/callout-card.test.tsx b/packages/app/src/components/callout-card.test.tsx index 0cb0cc8c9..a5fc2e24a 100644 --- a/packages/app/src/components/callout-card.test.tsx +++ b/packages/app/src/components/callout-card.test.tsx @@ -57,6 +57,22 @@ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); import { CalloutCard } from "./callout-card"; +type CalloutCardActions = React.ComponentProps["actions"]; + +function buildSingleAction(onPress: () => void): CalloutCardActions { + return [{ label: "Undo", onPress }]; +} + +function buildTwoActions( + onWhatsNew: () => void, + onInstall: () => void, +): CalloutCardActions { + return [ + { label: "What's new", onPress: onWhatsNew }, + { label: "Install & restart", onPress: onInstall, variant: "primary" }, + ]; +} + describe("CalloutCard", () => { let root: Root | null = null; let container: HTMLElement | null = null; @@ -101,7 +117,7 @@ describe("CalloutCard", () => { it("renders one action when one is provided", () => { const onPress = vi.fn(); - const actions = [{ label: "Undo", onPress }]; + const actions = buildSingleAction(onPress); act(() => { root?.render(); }); @@ -114,10 +130,7 @@ describe("CalloutCard", () => { }); it("renders up to two actions", () => { - const actions: React.ComponentProps["actions"] = [ - { label: "What's new", onPress: vi.fn() }, - { label: "Install & restart", onPress: vi.fn(), variant: "primary" }, - ]; + const actions = buildTwoActions(vi.fn(), vi.fn()); act(() => { root?.render( { return { DropdownMenu: ({ children }: { children: React.ReactNode }) => { const [open, setOpen] = React.useState(false); - return ( - {children} - ); + const contextValue = React.useMemo(() => ({ open, setOpen }), [open]); + return {children}; }, DropdownMenuTrigger: ({ children, diff --git a/packages/app/src/components/message-input.test.tsx b/packages/app/src/components/message-input.test.tsx index 80f858f16..6d814e76f 100644 --- a/packages/app/src/components/message-input.test.tsx +++ b/packages/app/src/components/message-input.test.tsx @@ -5,6 +5,10 @@ import { JSDOM } from "jsdom"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { MessageInput, type AttachmentMenuItem, type MessageInputRef } from "./message-input"; +const EMPTY_ATTACHMENTS: React.ComponentProps["attachments"] = []; +const EMPTY_ATTACHMENT_MENU_ITEMS: AttachmentMenuItem[] = []; +const FAKE_CONNECTED_CLIENT = { isConnected: true } as never; + const { startDictationMock, cancelDictationMock, confirmDictationMock } = vi.hoisted(() => ({ startDictationMock: vi.fn(), cancelDictationMock: vi.fn(), @@ -251,10 +255,10 @@ function renderMessageInput( value={value} onChangeText={vi.fn()} onSubmit={vi.fn()} - attachments={[]} + attachments={EMPTY_ATTACHMENTS} cwd="/repo" attachmentMenuItems={menuItems} - client={{ isConnected: true } as never} + client={FAKE_CONNECTED_CLIENT} isAgentRunning={false} submitIcon={submitIcon} onQueue={vi.fn()} @@ -346,10 +350,10 @@ describe("MessageInput dictation shortcuts", () => { value="" onChangeText={vi.fn()} onSubmit={vi.fn()} - attachments={[]} + attachments={EMPTY_ATTACHMENTS} cwd="/repo" - attachmentMenuItems={[]} - client={{ isConnected: true } as never} + attachmentMenuItems={EMPTY_ATTACHMENT_MENU_ITEMS} + client={FAKE_CONNECTED_CLIENT} isAgentRunning={false} isReadyForDictation={false} onQueue={vi.fn()} @@ -370,10 +374,10 @@ describe("MessageInput dictation shortcuts", () => { value="" onChangeText={vi.fn()} onSubmit={vi.fn()} - attachments={[]} + attachments={EMPTY_ATTACHMENTS} cwd="/repo" - attachmentMenuItems={[]} - client={{ isConnected: true } as never} + attachmentMenuItems={EMPTY_ATTACHMENT_MENU_ITEMS} + client={FAKE_CONNECTED_CLIENT} isAgentRunning={false} isReadyForDictation onQueue={vi.fn()} diff --git a/packages/app/src/components/stream-strategy-web.test.tsx b/packages/app/src/components/stream-strategy-web.test.tsx index 3ccd1a82a..1b49e9c8a 100644 --- a/packages/app/src/components/stream-strategy-web.test.tsx +++ b/packages/app/src/components/stream-strategy-web.test.tsx @@ -18,11 +18,13 @@ function userMessage(index: number): StreamItem { }; } +const VIRTUAL_ROW_STYLE = { height: 24 }; + function createRenderers(onRowRender: () => void): StreamSegmentRenderers { return { renderHistoryVirtualizedRow: (item) => { onRowRender(); - return
{item.id}
; + return
{item.id}
; }, renderHistoryMountedRow: (item) =>
{item.id}
, renderLiveHeadRow: (item) =>
{item.id}
, diff --git a/packages/app/src/panels/agent-panel.test.tsx b/packages/app/src/panels/agent-panel.test.tsx index daa288b48..7427978e8 100644 --- a/packages/app/src/panels/agent-panel.test.tsx +++ b/packages/app/src/panels/agent-panel.test.tsx @@ -294,6 +294,19 @@ function seedReadyAgent(agent: Agent = makeAgent()) { store.setAgentAuthoritativeHistoryApplied("server", "agent", true); } +function buildTestPaneValue() { + return { + serverId: "server", + workspaceId: "workspace", + tabId: "agent-agent", + target: { kind: "agent" as const, agentId: "agent" }, + openTab: vi.fn(), + closeCurrentTab: vi.fn(), + retargetCurrentTab: vi.fn(), + openFileInWorkspace: vi.fn(), + }; +} + async function renderAgentPanel( root: Root, focus: PaneFocusContextValue = { @@ -309,21 +322,11 @@ async function renderAgentPanel( mutations: { retry: false }, }, }); + const paneValue = buildTestPaneValue(); await act(async () => { root.render( - + diff --git a/packages/app/src/screens/new-workspace-screen.test.tsx b/packages/app/src/screens/new-workspace-screen.test.tsx index e74bcb977..2f497cc19 100644 --- a/packages/app/src/screens/new-workspace-screen.test.tsx +++ b/packages/app/src/screens/new-workspace-screen.test.tsx @@ -17,7 +17,7 @@ const { saveDraftInputMock, clearDraftInputMock, queueDraftSubmissionMock, - createdAgent, + createdAgent: _createdAgent, createdWorkspace, prItem, prItemB, @@ -163,6 +163,13 @@ vi.mock("lucide-react-native", () => { }; }); +function flattenReanimatedStyle(style: unknown) { + if (!Array.isArray(style)) { + return style; + } + return Object.assign({}, ...style.filter(Boolean)); +} + vi.mock("react-native-reanimated", () => ({ default: { View: ({ @@ -170,9 +177,7 @@ vi.mock("react-native-reanimated", () => ({ style, ...props }: React.HTMLAttributes & { testID?: string; style?: unknown }) => { - const flattenedStyle = Array.isArray(style) - ? Object.assign({}, ...style.filter(Boolean)) - : style; + const flattenedStyle = flattenReanimatedStyle(style); return
; }, },