feat(UI): Adding linter and prettier for UI (#3156)

This commit is contained in:
Francisco Arceo 2025-08-14 15:58:43 -06:00 committed by GitHub
parent 61582f327c
commit e69acbafbf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
73 changed files with 1452 additions and 1226 deletions

View file

@ -22,7 +22,7 @@ export function GroupedItemsDisplay({
return (
<>
{groupedItems.map((groupedItem) => {
{groupedItems.map(groupedItem => {
// If this is a function call with an output, render the grouped component
if (
groupedItem.outputItem &&

View file

@ -18,7 +18,7 @@ export interface GroupedItem {
* @returns Array of grouped items with their outputs
*/
export function useFunctionCallGrouping(
items: AnyResponseItem[],
items: AnyResponseItem[]
): GroupedItem[] {
return useMemo(() => {
const groupedItems: GroupedItem[] = [];

View file

@ -52,7 +52,7 @@ export function ItemRenderer({
// Fallback to generic item for unknown types
return (
<GenericItemComponent
item={item as any}
item={item as Record<string, unknown>}
index={index}
keyPrefix={keyPrefix}
/>

View file

@ -20,7 +20,7 @@ export function MessageItemComponent({
content = item.content;
} else if (Array.isArray(item.content)) {
content = item.content
.map((c) => {
.map(c => {
return c.type === "input_text" || c.type === "output_text"
? c.text
: JSON.stringify(c);

View file

@ -18,7 +18,7 @@ describe("ResponseDetailView", () => {
describe("Loading State", () => {
test("renders loading skeleton when isLoading is true", () => {
const { container } = render(
<ResponseDetailView {...defaultProps} isLoading={true} />,
<ResponseDetailView {...defaultProps} isLoading={true} />
);
// Check for skeleton elements
@ -36,13 +36,13 @@ describe("ResponseDetailView", () => {
<ResponseDetailView
{...defaultProps}
error={{ name: "Error", message: errorMessage }}
/>,
/>
);
expect(screen.getByText("Responses Details")).toBeInTheDocument();
// The error message is split across elements, so we check for parts
expect(
screen.getByText(/Error loading details for ID/),
screen.getByText(/Error loading details for ID/)
).toBeInTheDocument();
expect(screen.getByText(/test_id/)).toBeInTheDocument();
expect(screen.getByText(/Network Error/)).toBeInTheDocument();
@ -53,11 +53,11 @@ describe("ResponseDetailView", () => {
<ResponseDetailView
{...defaultProps}
error={{ name: "Error", message: "" }}
/>,
/>
);
expect(
screen.getByText(/Error loading details for ID/),
screen.getByText(/Error loading details for ID/)
).toBeInTheDocument();
expect(screen.getByText(/test_id/)).toBeInTheDocument();
});
@ -124,14 +124,14 @@ describe("ResponseDetailView", () => {
// Check properties - use regex to handle text split across elements
expect(screen.getByText(/Created/)).toBeInTheDocument();
expect(
screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
// Check for the specific ID label (not Previous Response ID)
expect(
screen.getByText((content, element) => {
return element?.tagName === "STRONG" && content === "ID:";
}),
})
).toBeInTheDocument();
expect(screen.getByText("resp_123")).toBeInTheDocument();
@ -166,7 +166,7 @@ describe("ResponseDetailView", () => {
};
render(
<ResponseDetailView {...defaultProps} response={minimalResponse} />,
<ResponseDetailView {...defaultProps} response={minimalResponse} />
);
// Should show required properties
@ -179,7 +179,7 @@ describe("ResponseDetailView", () => {
expect(screen.queryByText("Top P")).not.toBeInTheDocument();
expect(screen.queryByText("Parallel Tool Calls")).not.toBeInTheDocument();
expect(
screen.queryByText("Previous Response ID"),
screen.queryByText("Previous Response ID")
).not.toBeInTheDocument();
});
@ -196,7 +196,7 @@ describe("ResponseDetailView", () => {
// The error is shown in the properties sidebar, not as a separate "Error" label
expect(
screen.getByText("invalid_request: The request was invalid"),
screen.getByText("invalid_request: The request was invalid")
).toBeInTheDocument();
});
});
@ -218,7 +218,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
isLoadingInputItems={true}
/>,
/>
);
// Check for skeleton loading in input items section
@ -227,7 +227,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
isLoadingInputItems={true}
/>,
/>
);
const skeletons = container.querySelectorAll('[data-slot="skeleton"]');
@ -243,16 +243,16 @@ describe("ResponseDetailView", () => {
name: "Error",
message: "Failed to load input items",
}}
/>,
/>
);
expect(
screen.getByText(
"Error loading input items: Failed to load input items",
),
"Error loading input items: Failed to load input items"
)
).toBeInTheDocument();
expect(
screen.getByText("Falling back to response input data."),
screen.getByText("Falling back to response input data.")
).toBeInTheDocument();
// Should still show fallback input data
@ -276,7 +276,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
inputItems={mockInputItems}
/>,
/>
);
// Should show input items data, not response.input
@ -295,7 +295,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
inputItems={emptyInputItems}
/>,
/>
);
// Should show fallback input data
@ -313,7 +313,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={responseWithoutInput}
inputItems={null}
/>,
/>
);
expect(screen.getByText("No input data available.")).toBeInTheDocument();
@ -443,7 +443,7 @@ describe("ResponseDetailView", () => {
render(<ResponseDetailView {...defaultProps} response={mockResponse} />);
expect(
screen.getByText('input_function({"param": "value"})'),
screen.getByText('input_function({"param": "value"})')
).toBeInTheDocument();
expect(screen.getByText("Function Call")).toBeInTheDocument();
});
@ -468,7 +468,7 @@ describe("ResponseDetailView", () => {
render(<ResponseDetailView {...defaultProps} response={mockResponse} />);
expect(
screen.getByText("web_search_call(status: completed)"),
screen.getByText("web_search_call(status: completed)")
).toBeInTheDocument();
expect(screen.getByText("Function Call")).toBeInTheDocument();
expect(screen.getByText("(Web Search)")).toBeInTheDocument();
@ -522,7 +522,7 @@ describe("ResponseDetailView", () => {
render(<ResponseDetailView {...defaultProps} response={mockResponse} />);
expect(
screen.getByText("First output Second output"),
screen.getByText("First output Second output")
).toBeInTheDocument();
expect(screen.getByText("Assistant")).toBeInTheDocument();
});
@ -549,7 +549,7 @@ describe("ResponseDetailView", () => {
render(<ResponseDetailView {...defaultProps} response={mockResponse} />);
expect(
screen.getByText('search_function({"query": "test"})'),
screen.getByText('search_function({"query": "test"})')
).toBeInTheDocument();
expect(screen.getByText("Function Call")).toBeInTheDocument();
});
@ -598,7 +598,7 @@ describe("ResponseDetailView", () => {
render(<ResponseDetailView {...defaultProps} response={mockResponse} />);
expect(
screen.getByText("web_search_call(status: completed)"),
screen.getByText("web_search_call(status: completed)")
).toBeInTheDocument();
expect(screen.getByText(/Function Call/)).toBeInTheDocument();
expect(screen.getByText("(Web Search)")).toBeInTheDocument();
@ -616,7 +616,7 @@ describe("ResponseDetailView", () => {
type: "unknown_type",
custom_field: "custom_value",
data: { nested: "object" },
} as any,
} as unknown,
],
input: [],
};
@ -625,7 +625,7 @@ describe("ResponseDetailView", () => {
// Should show JSON stringified content
expect(
screen.getByText(/custom_field.*custom_value/),
screen.getByText(/custom_field.*custom_value/)
).toBeInTheDocument();
expect(screen.getByText("(unknown_type)")).toBeInTheDocument();
});
@ -666,7 +666,7 @@ describe("ResponseDetailView", () => {
role: "assistant",
call_id: "call_123",
content: "sunny and warm",
} as any, // Using any to bypass the type restriction for this test
} as unknown, // Using any to bypass the type restriction for this test
],
input: [],
};
@ -676,7 +676,7 @@ describe("ResponseDetailView", () => {
// Should show the function call and message as separate items (not grouped)
expect(screen.getByText("Function Call")).toBeInTheDocument();
expect(
screen.getByText('get_weather({"city": "Tokyo"})'),
screen.getByText('get_weather({"city": "Tokyo"})')
).toBeInTheDocument();
expect(screen.getByText("Assistant")).toBeInTheDocument();
expect(screen.getByText("sunny and warm")).toBeInTheDocument();
@ -706,7 +706,7 @@ describe("ResponseDetailView", () => {
status: "completed",
call_id: "call_123",
output: "sunny and warm",
} as any, // Using any to bypass the type restriction for this test
} as unknown,
],
input: [],
};
@ -717,7 +717,7 @@ describe("ResponseDetailView", () => {
expect(screen.getByText("Function Call")).toBeInTheDocument();
expect(screen.getByText("Arguments")).toBeInTheDocument();
expect(
screen.getByText('get_weather({"city": "Tokyo"})'),
screen.getByText('get_weather({"city": "Tokyo"})')
).toBeInTheDocument();
// Use getAllByText since there are multiple "Output" elements (card title and output label)
const outputElements = screen.getAllByText("Output");

View file

@ -146,7 +146,7 @@ describe("ResponsesTable", () => {
expect(tableCaption).toBeInTheDocument();
if (tableCaption) {
const captionSkeleton = tableCaption.querySelector(
'[data-slot="skeleton"]',
'[data-slot="skeleton"]'
);
expect(captionSkeleton).toBeInTheDocument();
}
@ -156,7 +156,7 @@ describe("ResponsesTable", () => {
expect(tableBody).toBeInTheDocument();
if (tableBody) {
const bodySkeletons = tableBody.querySelectorAll(
'[data-slot="skeleton"]',
'[data-slot="skeleton"]'
);
expect(bodySkeletons.length).toBeGreaterThan(0);
}
@ -176,14 +176,14 @@ describe("ResponsesTable", () => {
render(<ResponsesTable {...defaultProps} />);
expect(
screen.getByText("Unable to load chat completions"),
screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(screen.getByText(errorMessage)).toBeInTheDocument();
});
test.each([{ name: "Error", message: "" }, {}])(
"renders default error message when error has no message",
(errorObject) => {
errorObject => {
mockedUsePagination.mockReturnValue({
data: [],
status: "error",
@ -194,14 +194,14 @@ describe("ResponsesTable", () => {
render(<ResponsesTable {...defaultProps} />);
expect(
screen.getByText("Unable to load chat completions"),
screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(
screen.getByText(
"An unexpected error occurred while loading the data.",
),
"An unexpected error occurred while loading the data."
)
).toBeInTheDocument();
},
}
);
});
@ -275,7 +275,7 @@ describe("ResponsesTable", () => {
// Table caption
expect(
screen.getByText("A list of your recent responses."),
screen.getByText("A list of your recent responses.")
).toBeInTheDocument();
// Table headers
@ -289,14 +289,14 @@ describe("ResponsesTable", () => {
expect(screen.getByText("Test output")).toBeInTheDocument();
expect(screen.getByText("llama-test-model")).toBeInTheDocument();
expect(
screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
expect(screen.getByText("Another input")).toBeInTheDocument();
expect(screen.getByText("Another output")).toBeInTheDocument();
expect(screen.getByText("llama-another-model")).toBeInTheDocument();
expect(
screen.getByText(new Date(1710001000 * 1000).toLocaleString()),
screen.getByText(new Date(1710001000 * 1000).toLocaleString())
).toBeInTheDocument();
});
});
@ -487,7 +487,7 @@ describe("ResponsesTable", () => {
render(<ResponsesTable {...defaultProps} />);
expect(
screen.getByText('search_function({"query": "test"})'),
screen.getByText('search_function({"query": "test"})')
).toBeInTheDocument();
});
@ -548,7 +548,7 @@ describe("ResponsesTable", () => {
render(<ResponsesTable {...defaultProps} />);
expect(
screen.getByText("web_search_call(status: completed)"),
screen.getByText("web_search_call(status: completed)")
).toBeInTheDocument();
});
@ -565,7 +565,7 @@ describe("ResponsesTable", () => {
id: "unknown_123",
status: "completed",
custom_field: "custom_value",
} as any,
} as unknown,
],
input: [{ type: "message", content: "input" }],
};
@ -594,7 +594,7 @@ describe("ResponsesTable", () => {
{
type: "unknown_type",
data: "some data",
} as any,
} as unknown,
],
input: [{ type: "message", content: "input" }],
};
@ -623,7 +623,7 @@ describe("ResponsesTable", () => {
return typeof text === "string" && text.length > effectiveMaxLength
? text.slice(0, effectiveMaxLength) + "..."
: text;
},
}
);
const longInput =
@ -665,7 +665,7 @@ describe("ResponsesTable", () => {
// The truncated text should be present for both input and output
const truncatedTexts = screen.getAllByText(
longInput.slice(0, 10) + "...",
longInput.slice(0, 10) + "..."
);
expect(truncatedTexts.length).toBe(2); // one for input, one for output
});

View file

@ -27,7 +27,7 @@ interface ResponsesTableProps {
* Helper function to convert ResponseListResponse.Data to OpenAIResponse
*/
const convertResponseListData = (
responseData: ResponseListResponse.Data,
responseData: ResponseListResponse.Data
): OpenAIResponse => {
return {
id: responseData.id,
@ -56,8 +56,8 @@ function getInputText(response: OpenAIResponse): string {
}
function getOutputText(response: OpenAIResponse): string {
const firstMessage = response.output.find((item) =>
isMessageItem(item as any),
const firstMessage = response.output.find(item =>
isMessageItem(item as Record<string, unknown>)
);
if (firstMessage) {
const content = extractContentFromItem(firstMessage as MessageItem);
@ -66,15 +66,15 @@ function getOutputText(response: OpenAIResponse): string {
}
}
const functionCall = response.output.find((item) =>
isFunctionCallItem(item as any),
const functionCall = response.output.find(item =>
isFunctionCallItem(item as Record<string, unknown>)
);
if (functionCall) {
return formatFunctionCall(functionCall as FunctionCallItem);
}
const webSearchCall = response.output.find((item) =>
isWebSearchCallItem(item as any),
const webSearchCall = response.output.find(item =>
isWebSearchCallItem(item as Record<string, unknown>)
);
if (webSearchCall) {
return formatWebSearchCall(webSearchCall as WebSearchCallItem);
@ -95,7 +95,7 @@ function extractContentFromItem(item: {
} else if (Array.isArray(item.content)) {
const textContent = item.content.find(
(c: ResponseInputMessageContent) =>
c.type === "input_text" || c.type === "output_text",
c.type === "input_text" || c.type === "output_text"
);
return textContent?.text || "";
}
@ -131,14 +131,14 @@ export function ResponsesTable({ paginationOptions }: ResponsesTableProps) {
limit: number;
model?: string;
order?: string;
},
}
) => {
const response = await client.responses.list({
after: params.after,
limit: params.limit,
...(params.model && { model: params.model }),
...(params.order && { order: params.order }),
} as any);
} as Parameters<typeof client.responses.list>[0]);
const listResponse = response as ResponseListResponse;

View file

@ -29,7 +29,7 @@ export type AnyResponseItem =
| FunctionCallOutputItem;
export function isMessageInput(
item: ResponseInput,
item: ResponseInput
): item is ResponseInput & { type: "message" } {
return item.type === "message";
}
@ -39,23 +39,23 @@ export function isMessageItem(item: AnyResponseItem): item is MessageItem {
}
export function isFunctionCallItem(
item: AnyResponseItem,
item: AnyResponseItem
): item is FunctionCallItem {
return item.type === "function_call" && "name" in item;
}
export function isWebSearchCallItem(
item: AnyResponseItem,
item: AnyResponseItem
): item is WebSearchCallItem {
return item.type === "web_search_call";
}
export function isFunctionCallOutputItem(
item: AnyResponseItem,
item: AnyResponseItem
): item is FunctionCallOutputItem {
return (
item.type === "function_call_output" &&
"call_id" in item &&
typeof (item as any).call_id === "string"
typeof (item as Record<string, unknown>).call_id === "string"
);
}