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.
This commit is contained in:
Mohamed Boudra
2026-04-24 02:52:23 +07:00
parent 2ee4955cbe
commit e313bf5776
6 changed files with 59 additions and 33 deletions

View File

@@ -57,6 +57,22 @@ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
import { CalloutCard } from "./callout-card";
type CalloutCardActions = React.ComponentProps<typeof CalloutCard>["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(<CalloutCard description="Saved." actions={actions} testID="callout" />);
});
@@ -114,10 +130,7 @@ describe("CalloutCard", () => {
});
it("renders up to two actions", () => {
const actions: React.ComponentProps<typeof CalloutCard>["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(
<CalloutCard

View File

@@ -441,9 +441,8 @@ vi.mock("@/components/ui/dropdown-menu", () => {
return {
DropdownMenu: ({ children }: { children: React.ReactNode }) => {
const [open, setOpen] = React.useState(false);
return (
<DropdownContext.Provider value={{ open, setOpen }}>{children}</DropdownContext.Provider>
);
const contextValue = React.useMemo(() => ({ open, setOpen }), [open]);
return <DropdownContext.Provider value={contextValue}>{children}</DropdownContext.Provider>;
},
DropdownMenuTrigger: ({
children,

View File

@@ -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<typeof MessageInput>["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()}

View File

@@ -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 <div style={{ height: 24 }}>{item.id}</div>;
return <div style={VIRTUAL_ROW_STYLE}>{item.id}</div>;
},
renderHistoryMountedRow: (item) => <div>{item.id}</div>,
renderLiveHeadRow: (item) => <div>{item.id}</div>,

View File

@@ -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(
<QueryClientProvider client={queryClient}>
<PaneProvider
value={{
serverId: "server",
workspaceId: "workspace",
tabId: "agent-agent",
target: { kind: "agent", agentId: "agent" },
openTab: vi.fn(),
closeCurrentTab: vi.fn(),
retargetCurrentTab: vi.fn(),
openFileInWorkspace: vi.fn(),
}}
>
<PaneProvider value={paneValue}>
<PaneFocusProvider value={focus}>
<AgentPanel />
</PaneFocusProvider>

View File

@@ -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<HTMLDivElement> & { testID?: string; style?: unknown }) => {
const flattenedStyle = Array.isArray(style)
? Object.assign({}, ...style.filter(Boolean))
: style;
const flattenedStyle = flattenReanimatedStyle(style);
return <div {...props} data-testid={testID} style={flattenedStyle as React.CSSProperties} />;
},
},